context: //---------------------------------------------------------------------------
  //
  // Copyright (C) Microsoft Corporation.  All rights reserved.
  //
  //---------------------------------------------------------------------------
  
  using System;
  using System.ComponentModel;
  using System.Diagnostics;
  using System.Reflection;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Data;
  using System.Windows.Input;
  using MS.Internal;
  
  namespace Microsoft.Windows.Controls
  {
      /// <summary>
      ///     A base class for specifying the column definitions.
      /// </summary>
      public abstract class DataGridColumn : DependencyObject
      {
          #region Header
  
          /// <summary>
          ///     An object that represents the header of this column.
          /// </summary>
          public object Header
          {
              get { return (object)GetValue(HeaderProperty); }
              set { SetValue(HeaderProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the Header property.
          /// </summary>
          public static readonly DependencyProperty HeaderProperty =
              DependencyProperty.Register("Header", typeof(object), typeof(DataGridColumn), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyColumnHeaderPropertyChanged)));
  
          /// <summary>
          ///     The Style for the DataGridColumnHeader
          /// </summary>
          public Style HeaderStyle
          {
              get { return (Style)GetValue(HeaderStyleProperty); }
              set { SetValue(HeaderStyleProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the HeaderStyle property.
          /// </summary>
          public static readonly DependencyProperty HeaderStyleProperty =
              DependencyProperty.Register("HeaderStyle", typeof(Style), typeof(DataGridColumn), new FrameworkPropertyMetadata(null, OnNotifyColumnHeaderPropertyChanged, OnCoerceHeaderStyle));
  
          private static object OnCoerceHeaderStyle(DependencyObject d, object baseValue)
          {
              var column = d as DataGridColumn;
              return DataGridHelper.GetCoercedTransferPropertyValue(
                  column, 
                  baseValue, 
                  HeaderStyleProperty,
                  column.DataGridOwner, 
                  DataGrid.ColumnHeaderStyleProperty);
          }
  
          /// <summary>
          ///     The string format to apply to the header.
          /// </summary>
          public string HeaderStringFormat
          {
              get { return (string)GetValue(HeaderStringFormatProperty); }
              set { SetValue(HeaderStringFormatProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the HeaderStringFormat property.
          /// </summary>
          public static readonly DependencyProperty HeaderStringFormatProperty =
              DependencyProperty.Register("HeaderStringFormat", typeof(string), typeof(DataGridColumn), new FrameworkPropertyMetadata(null, OnNotifyColumnHeaderPropertyChanged));
  
          /// <summary>
          ///     The template that defines the visual representation of the header.
          /// </summary>
          public DataTemplate HeaderTemplate
          {
              get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
              set { SetValue(HeaderTemplateProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the HeaderTemplate property.
          /// </summary>
          public static readonly DependencyProperty HeaderTemplateProperty =
              DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(DataGridColumn), new FrameworkPropertyMetadata(null, OnNotifyColumnHeaderPropertyChanged));
  
          /// <summary>
          ///     DataTemplateSelector that selects which template to use for the Column Header
          /// </summary>
          public DataTemplateSelector HeaderTemplateSelector
          {
              get { return (DataTemplateSelector)GetValue(HeaderTemplateSelectorProperty); }
              set { SetValue(HeaderTemplateSelectorProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the HeaderTemplateSelector property.
          /// </summary>
          public static readonly DependencyProperty HeaderTemplateSelectorProperty =
              DependencyProperty.Register("HeaderTemplateSelector", typeof(DataTemplateSelector), typeof(DataGridColumn), new FrameworkPropertyMetadata(null, OnNotifyColumnHeaderPropertyChanged));
  
          #endregion
  
          #region Cell Container
  
          /// <summary>
          ///     A style to apply to the container of cells in this column.
          /// </summary>
          public Style CellStyle
          {
              get { return (Style)GetValue(CellStyleProperty); }
              set { SetValue(CellStyleProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the CellStyle property.
          /// </summary>
          public static readonly DependencyProperty CellStyleProperty =
              DependencyProperty.Register("CellStyle", typeof(Style), typeof(DataGridColumn), new FrameworkPropertyMetadata(null, OnNotifyCellPropertyChanged, OnCoerceCellStyle));
  
          private static object OnCoerceCellStyle(DependencyObject d, object baseValue)
          {
              var column = d as DataGridColumn;
              return DataGridHelper.GetCoercedTransferPropertyValue(
                  column, 
                  baseValue, 
                  CellStyleProperty,                
                  column.DataGridOwner, 
                  DataGrid.CellStyleProperty);
          }
  
          /// <summary>
          ///     Whether cells in this column can enter edit mode.
          /// </summary>
          public bool IsReadOnly
          {
              get { return (bool)GetValue(IsReadOnlyProperty); }
              set { SetValue(IsReadOnlyProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the IsReadOnly property.
          /// </summary>
          public static readonly DependencyProperty IsReadOnlyProperty =
              DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(DataGridColumn), new FrameworkPropertyMetadata(false, OnNotifyCellPropertyChanged, OnCoerceIsReadOnly));
  
          private static object OnCoerceIsReadOnly(DependencyObject d, object baseValue)
          {
              var column = d as DataGridColumn;
  
              return column.OnCoerceIsReadOnly((bool)baseValue);
          }
  
          /// <summary>
          ///     Subtypes can override this to force IsReadOnly to be coerced to true.
          /// </summary>
          protected virtual bool OnCoerceIsReadOnly(bool baseValue)
          {
              return (bool)DataGridHelper.GetCoercedTransferPropertyValue(
                  this,
                  baseValue,
                  IsReadOnlyProperty,
                  DataGridOwner,
                  DataGrid.IsReadOnlyProperty);
          }
  
          #endregion
  
          #region Width
  
          /// <summary>
          ///     Specifies the width of the header and cells within this column.
          /// </summary>
          public DataGridLength Width
          {
              get { return (DataGridLength)GetValue(WidthProperty); }
              set { SetValue(WidthProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the Width property.
          /// </summary>
          public static readonly DependencyProperty WidthProperty =
              DependencyProperty.Register(
                  "Width", 
                  typeof(DataGridLength), 
                  typeof(DataGridColumn), 
                  new FrameworkPropertyMetadata(DataGridLength.Auto, new PropertyChangedCallback(OnWidthPropertyChanged), new CoerceValueCallback(OnCoerceWidth)));
  
          /// <summary>
          /// Internal method which sets the column's width
          /// without actual redistribution of widths among other
          /// columns
          /// </summary>
          /// <param name="width"></param>
          internal void SetWidthInternal(DataGridLength width)
          {
              bool originalValue = _ignoreRedistributionOnWidthChange;
              _ignoreRedistributionOnWidthChange = true;
              try
              {
                  Width = width;
              }
              finally
              {
                  _ignoreRedistributionOnWidthChange = originalValue;
              }
          }
  
          /// <summary>
          /// Property changed call back for Width property which notification propagation
          /// and does the redistribution of widths among other columns if needed
          /// </summary>
          /// <param name="d"></param>
          /// <param name="e"></param>
          private static void OnWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              DataGridColumn column = (DataGridColumn)d;
              DataGridLength oldWidth = (DataGridLength)e.OldValue;
              DataGridLength newWidth = (DataGridLength)e.NewValue;
              DataGrid dataGrid = column.DataGridOwner;
  
              if (dataGrid != null &&
                  !DoubleUtil.AreClose(oldWidth.DisplayValue, newWidth.DisplayValue))
              {
                  dataGrid.InternalColumns.InvalidateAverageColumnWidth();
              }
  
              if (column._processingWidthChange)
              {
                  column.CoerceValue(ActualWidthProperty);
                  return;
              }
  
              column._processingWidthChange = true;
              if (oldWidth.IsStar != newWidth.IsStar)
              {
                  column.CoerceValue(MaxWidthProperty);
              }
  
              try
              {
                  if (dataGrid != null && (newWidth.IsStar ^ oldWidth.IsStar))
                  {
                      dataGrid.InternalColumns.InvalidateHasVisibleStarColumns();
                  }
  
                  column.NotifyPropertyChanged(
                      d, 
                      e, 
                      NotificationTarget.ColumnCollection |
                      NotificationTarget.Columns | 
                      NotificationTarget.Cells | 
                      NotificationTarget.ColumnHeaders |
                      NotificationTarget.CellsPresenter |
                      NotificationTarget.ColumnHeadersPresenter);
  
                  if (dataGrid != null)
                  {
                      if (!column._ignoreRedistributionOnWidthChange && column.IsVisible)
                      {
                          if (!newWidth.IsStar && !newWidth.IsAbsolute)
                          {
                              DataGridLength changedWidth = column.Width;
                              double displayValue = DataGridHelper.CoerceToMinMax(changedWidth.DesiredValue, column.MinWidth, column.MaxWidth);
                              column.SetWidthInternal(new DataGridLength(changedWidth.Value, changedWidth.UnitType, changedWidth.DesiredValue, displayValue));
                          }
  
                          dataGrid.InternalColumns.RedistributeColumnWidthsOnWidthChangeOfColumn(column, (DataGridLength)e.OldValue);
                      }
                  }
              }
              finally
              {
                  column._processingWidthChange = false;
              }
          }
  
          /// <summary>
          ///     Specifies the minimum width of the header and cells within this column.
          /// </summary>
          public double MinWidth
          {
              get { return (double)GetValue(MinWidthProperty); }
              set { SetValue(MinWidthProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the MinWidth property.
          /// </summary>
          public static readonly DependencyProperty MinWidthProperty =
              DependencyProperty.Register(
                  "MinWidth", 
                  typeof(double), 
                  typeof(DataGridColumn),
                  new FrameworkPropertyMetadata(20d, new PropertyChangedCallback(OnMinWidthPropertyChanged), new CoerceValueCallback(OnCoerceMinWidth)), 
                  new ValidateValueCallback(ValidateMinWidth));
  
          /// <summary>
          /// Property changed call back for MinWidth property which notification propagation
          /// and does the redistribution of widths among other columns if needed
          /// </summary>
          /// <param name="d"></param>
          /// <param name="e"></param>
          private static void OnMinWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              DataGridColumn column = (DataGridColumn)d;
              DataGrid dataGrid = column.DataGridOwner;
  
              column.NotifyPropertyChanged(d, e, NotificationTarget.Columns);
  
              if (dataGrid != null && column.IsVisible)
              {
                  dataGrid.InternalColumns.RedistributeColumnWidthsOnMinWidthChangeOfColumn(column, (double)e.OldValue);
              }
          }
  
          /// <summary>
          ///     Specifies the maximum width of the header and cells within this column.
          /// </summary>
          public double MaxWidth
          {
              get { return (double)GetValue(MaxWidthProperty); }
              set { SetValue(MaxWidthProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty that represents the MaxWidth property.
          /// </summary>
          public static readonly DependencyProperty MaxWidthProperty =
              DependencyProperty.Register(
                  "MaxWidth", 
                  typeof(double), 
                  typeof(DataGridColumn),
                  new FrameworkPropertyMetadata(double.PositiveInfinity, new PropertyChangedCallback(OnMaxWidthPropertyChanged), new CoerceValueCallback(OnCoerceMaxWidth)),
                  new ValidateValueCallback(ValidateMaxWidth));
  
          /// <summary>
          /// Property changed call back for MaxWidth property which notification propagation
          /// and does the redistribution of widths among other columns if needed
          /// </summary>
          /// <param name="d"></param>
          /// <param name="e"></param>
          private static void OnMaxWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              DataGridColumn column = (DataGridColumn)d;
              DataGrid dataGrid = column.DataGridOwner;
  
              column.NotifyPropertyChanged(d, e, NotificationTarget.Columns);
  
              if (dataGrid != null && column.IsVisible)
              {
                  dataGrid.InternalColumns.RedistributeColumnWidthsOnMaxWidthChangeOfColumn(column, (double)e.OldValue);
              }
          }
  
          /// <summary>
          ///     Helper method which coerces the DesiredValue or DisplayValue
          ///     of the width.
          /// </summary>
          private static double CoerceDesiredOrDisplayWidthValue(double widthValue, double memberValue, DataGridLengthUnitType type)
          {
              if (DoubleUtil.IsNaN(memberValue))
              {
                  if (type == DataGridLengthUnitType.Pixel)
                  {
                      memberValue = widthValue;
                  }
                  else if (type == DataGridLengthUnitType.Auto ||
                      type == DataGridLengthUnitType.SizeToCells ||
                      type == DataGridLengthUnitType.SizeToHeader)
                  {
                      memberValue = 0d;
                  }
              }
              return memberValue;
          }
  
          /// <summary>
          ///     Coerces the WidthProperty based on the DataGrid transferred property rules
          /// </summary>
          private static object OnCoerceWidth(DependencyObject d, object baseValue)
          {
              var column = d as DataGridColumn;
              DataGridLength width = (DataGridLength)DataGridHelper.GetCoercedTransferPropertyValue(
                  column, 
                  baseValue, 
                  WidthProperty,
                  column.DataGridOwner, 
                  DataGrid.ColumnWidthProperty);
  
              double newDesiredValue = CoerceDesiredOrDisplayWidthValue(width.Value, width.DesiredValue, width.UnitType);
              double newDisplayValue = CoerceDesiredOrDisplayWidthValue(width.Value, width.DisplayValue, width.UnitType);
              newDisplayValue = (DoubleUtil.IsNaN(newDisplayValue) ? newDisplayValue : DataGridHelper.CoerceToMinMax(newDisplayValue, column.MinWidth, column.MaxWidth));
              if (DoubleUtil.IsNaN(newDisplayValue) || DoubleUtil.AreClose(newDisplayValue, width.DisplayValue))
              {
                  return width;
              }
  
              return new DataGridLength(
                  width.Value,
                  width.UnitType,
                  newDesiredValue,
                  newDisplayValue);
          }
  
          /// <summary>
          ///     Coerces the MinWidthProperty based on the DataGrid transferred property rules
          /// </summary>
          private static object OnCoerceMinWidth(DependencyObject d, object baseValue)
          {
              var column = d as DataGridColumn;
              return DataGridHelper.GetCoercedTransferPropertyValue(
                  column, 
                  baseValue, 
                  MinWidthProperty,                
                  column.DataGridOwner, 
                  DataGrid.MinColumnWidthProperty);
          }
  
          /// <summary>
          ///     Coerces the MaxWidthProperty based on the DataGrid transferred property rules
          /// </summary>
          private static object OnCoerceMaxWidth(DependencyObject d, object baseValue)
          {
              var column = d as DataGridColumn;
              double transferValue =  (double)DataGridHelper.GetCoercedTransferPropertyValue(
                  column, 
                  baseValue, 
                  MaxWidthProperty,
                  column.DataGridOwner, 
                  DataGrid.MaxColumnWidthProperty);
  
              // Coerce the Max Width to 10k pixels if infinity on a star column
              if (double.IsPositiveInfinity(transferValue) &&
                  column.Width.IsStar)
              {
                  return _starMaxWidth;
              }
  
              return transferValue;
          }
  
          /// <summary>
          ///     Validates that the minimum width is an acceptable value
          /// </summary>
          private static bool ValidateMinWidth(object v)
          {
              double value = (double)v;
              return !(value < 0d || DoubleUtil.IsNaN(value) || Double.IsPositiveInfinity(value));
          }
  
          /// <summary>
          ///     Validates that the maximum width is an acceptable value
          /// </summary>
          private static bool ValidateMaxWidth(object v)
          {
              double value = (double)v;
  
gt:             return !(value < 0d || DoubleUtil.IsNaN(value));