2435 lines
98 KiB
C#
2435 lines
98 KiB
C#
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.Diagnostics;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Threading;
|
|
using MS.Internal;
|
|
using EGC = ExtendedGrid.Microsoft.Windows.Controls;
|
|
|
|
namespace ExtendedGrid.Microsoft.Windows.Controls
|
|
{
|
|
/// <summary>
|
|
/// Internal class that holds the DataGrid's column collection. Handles error-checking columns as they come in.
|
|
/// </summary>
|
|
internal class DataGridColumnCollection : ObservableCollection<EGC.DataGridColumn>
|
|
{
|
|
internal DataGridColumnCollection(EGC.DataGrid dataGridOwner)
|
|
{
|
|
Debug.Assert(dataGridOwner != null, "We should have a valid DataGrid");
|
|
|
|
DisplayIndexMap = new List<int>(5);
|
|
_dataGridOwner = dataGridOwner;
|
|
|
|
RealizedColumnsBlockListForNonVirtualizedRows = null;
|
|
RealizedColumnsDisplayIndexBlockListForNonVirtualizedRows = null;
|
|
RebuildRealizedColumnsBlockListForNonVirtualizedRows = true;
|
|
|
|
RealizedColumnsBlockListForVirtualizedRows = null;
|
|
RealizedColumnsDisplayIndexBlockListForVirtualizedRows = null;
|
|
RebuildRealizedColumnsBlockListForVirtualizedRows = true;
|
|
}
|
|
|
|
#region Protected Overrides
|
|
|
|
protected override void InsertItem(int index, EGC.DataGridColumn item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
throw new ArgumentNullException("item", SR.Get(SRID.DataGrid_NullColumn));
|
|
}
|
|
|
|
if (item.DataGridOwner != null)
|
|
{
|
|
throw new ArgumentException(SR.Get(SRID.DataGrid_InvalidColumnReuse, item.Header), "item");
|
|
}
|
|
|
|
if (DisplayIndexMapInitialized)
|
|
{
|
|
ValidateDisplayIndex(item, item.DisplayIndex, true);
|
|
}
|
|
|
|
base.InsertItem(index, item);
|
|
item.CoerceValue(EGC.DataGridColumn.IsFrozenProperty);
|
|
}
|
|
|
|
protected override void SetItem(int index, EGC.DataGridColumn item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
throw new ArgumentNullException("item", SR.Get(SRID.DataGrid_NullColumn));
|
|
}
|
|
|
|
if (index >= Count || index < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException("index", SR.Get(SRID.DataGrid_ColumnIndexOutOfRange, item.Header));
|
|
}
|
|
|
|
if (item.DataGridOwner != null && this[index] != item)
|
|
{
|
|
throw new ArgumentException(SR.Get(SRID.DataGrid_InvalidColumnReuse, item.Header), "item");
|
|
}
|
|
|
|
if (DisplayIndexMapInitialized)
|
|
{
|
|
ValidateDisplayIndex(item, item.DisplayIndex);
|
|
}
|
|
|
|
base.SetItem(index, item);
|
|
item.CoerceValue(EGC.DataGridColumn.IsFrozenProperty);
|
|
}
|
|
|
|
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
|
{
|
|
switch (e.Action)
|
|
{
|
|
case NotifyCollectionChangedAction.Add:
|
|
if (DisplayIndexMapInitialized)
|
|
{
|
|
UpdateDisplayIndexForNewColumns(e.NewItems, e.NewStartingIndex);
|
|
}
|
|
|
|
InvalidateHasVisibleStarColumns();
|
|
break;
|
|
|
|
case NotifyCollectionChangedAction.Move:
|
|
if (DisplayIndexMapInitialized)
|
|
{
|
|
UpdateDisplayIndexForMovedColumn(e.OldStartingIndex, e.NewStartingIndex);
|
|
}
|
|
|
|
break;
|
|
|
|
case NotifyCollectionChangedAction.Remove:
|
|
if (DisplayIndexMapInitialized)
|
|
{
|
|
UpdateDisplayIndexForRemovedColumns(e.OldItems, e.OldStartingIndex);
|
|
}
|
|
|
|
ClearDisplayIndex(e.OldItems, e.NewItems);
|
|
InvalidateHasVisibleStarColumns();
|
|
break;
|
|
|
|
case NotifyCollectionChangedAction.Replace:
|
|
if (DisplayIndexMapInitialized)
|
|
{
|
|
UpdateDisplayIndexForReplacedColumn(e.OldItems, e.NewItems);
|
|
}
|
|
|
|
ClearDisplayIndex(e.OldItems, e.NewItems);
|
|
InvalidateHasVisibleStarColumns();
|
|
break;
|
|
|
|
case NotifyCollectionChangedAction.Reset:
|
|
// We dont ClearDisplayIndex here because we no longer have access to the old items.
|
|
// Instead this is handled in ClearItems.
|
|
if (DisplayIndexMapInitialized)
|
|
{
|
|
DisplayIndexMap.Clear();
|
|
DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Reset, -1, null, -1);
|
|
}
|
|
|
|
HasVisibleStarColumns = false;
|
|
break;
|
|
}
|
|
|
|
base.OnCollectionChanged(e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear's all the columns from this collection and resets DisplayIndex to its default value.
|
|
/// </summary>
|
|
protected override void ClearItems()
|
|
{
|
|
ClearDisplayIndex(this, null);
|
|
|
|
// Clear DataGrid reference is on all columns.
|
|
// Doing it here since CollectionChanged notification wouldn't
|
|
// propagate the cleared columns list.
|
|
DataGridOwner.UpdateDataGridReference(this, true);
|
|
base.ClearItems();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Notification Propagation
|
|
|
|
internal void NotifyPropertyChanged(DependencyObject d, string propertyName, DependencyPropertyChangedEventArgs e, NotificationTarget target)
|
|
{
|
|
if (EGC.DataGridHelper.ShouldNotifyColumnCollection(target))
|
|
{
|
|
if (e.Property == EGC.DataGridColumn.DisplayIndexProperty)
|
|
{
|
|
OnColumnDisplayIndexChanged((EGC.DataGridColumn)d, (int)e.OldValue, (int)e.NewValue);
|
|
if (((EGC.DataGridColumn)d).IsVisible)
|
|
{
|
|
InvalidateColumnRealization(true);
|
|
}
|
|
}
|
|
else if (e.Property == EGC.DataGridColumn.WidthProperty)
|
|
{
|
|
if (((EGC.DataGridColumn)d).IsVisible)
|
|
{
|
|
InvalidateColumnRealization(false);
|
|
}
|
|
}
|
|
else if (e.Property == EGC.DataGrid.FrozenColumnCountProperty)
|
|
{
|
|
InvalidateColumnRealization(false);
|
|
OnDataGridFrozenColumnCountChanged((int)e.OldValue, (int)e.NewValue);
|
|
}
|
|
else if (e.Property == EGC.DataGridColumn.VisibilityProperty)
|
|
{
|
|
InvalidateHasVisibleStarColumns();
|
|
InvalidateColumnWidthsComputation();
|
|
InvalidateColumnRealization(true);
|
|
}
|
|
else if (e.Property == EGC.DataGrid.EnableColumnVirtualizationProperty)
|
|
{
|
|
InvalidateColumnRealization(true);
|
|
}
|
|
else if (e.Property == EGC.DataGrid.CellsPanelHorizontalOffsetProperty)
|
|
{
|
|
OnCellsPanelHorizontalOffsetChanged(e);
|
|
}
|
|
else if (e.Property == EGC.DataGrid.HorizontalScrollOffsetProperty ||
|
|
string.Compare(propertyName, "ViewportWidth", StringComparison.Ordinal) == 0)
|
|
{
|
|
InvalidateColumnRealization(false);
|
|
}
|
|
}
|
|
|
|
if (EGC.DataGridHelper.ShouldNotifyColumns(target))
|
|
{
|
|
int count = this.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
// Passing in NotificationTarget.Columns directly to ensure the notification doesn't
|
|
// bounce back to the collection.
|
|
this[i].NotifyPropertyChanged(d, e, NotificationTarget.Columns);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Display Index
|
|
|
|
/// <summary>
|
|
/// Returns the DataGridColumn with the given DisplayIndex
|
|
/// </summary>
|
|
internal EGC.DataGridColumn ColumnFromDisplayIndex(int displayIndex)
|
|
{
|
|
Debug.Assert(displayIndex >= 0 && displayIndex < DisplayIndexMap.Count, "displayIndex should have already been validated");
|
|
return this[DisplayIndexMap[displayIndex]];
|
|
}
|
|
|
|
/// <summary>
|
|
/// A map of display index (key) to index in the column collection (value). Used to quickly find a column from its display index.
|
|
/// </summary>
|
|
internal List<int> DisplayIndexMap
|
|
{
|
|
get
|
|
{
|
|
if (!DisplayIndexMapInitialized)
|
|
{
|
|
InitializeDisplayIndexMap();
|
|
}
|
|
|
|
return _displayIndexMap;
|
|
}
|
|
|
|
private set
|
|
{
|
|
_displayIndexMap = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to guard against re-entrancy when changing the DisplayIndex of a column.
|
|
/// </summary>
|
|
private bool IsUpdatingDisplayIndex
|
|
{
|
|
get { return _isUpdatingDisplayIndex; }
|
|
set { _isUpdatingDisplayIndex = value; }
|
|
}
|
|
|
|
private int CoerceDefaultDisplayIndex(EGC.DataGridColumn column)
|
|
{
|
|
return CoerceDefaultDisplayIndex(column, IndexOf(column));
|
|
}
|
|
|
|
/// <summary>
|
|
/// This takes a column and checks that if its DisplayIndex is the default value. If so, it coerces
|
|
/// the DisplayIndex to be its location in the columns collection.
|
|
/// We can't do this in CoerceValue because the callback isn't called for default values. Instead we call this
|
|
/// whenever a column is added or replaced in the collection or when the DisplayIndex of an existing column has changed.
|
|
/// </summary>
|
|
/// <param name="column">The column</param>
|
|
/// <param name="newDisplayIndex">The DisplayIndex the column should have</param>
|
|
/// <returns>The DisplayIndex of the column</returns>
|
|
private int CoerceDefaultDisplayIndex(EGC.DataGridColumn column, int newDisplayIndex)
|
|
{
|
|
if (EGC.DataGridHelper.IsDefaultValue(column, EGC.DataGridColumn.DisplayIndexProperty))
|
|
{
|
|
bool isUpdating = IsUpdatingDisplayIndex;
|
|
try
|
|
{
|
|
IsUpdatingDisplayIndex = true;
|
|
column.DisplayIndex = newDisplayIndex;
|
|
}
|
|
finally
|
|
{
|
|
IsUpdatingDisplayIndex = isUpdating;
|
|
}
|
|
|
|
return newDisplayIndex;
|
|
}
|
|
|
|
return column.DisplayIndex;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when a column's display index has changed.
|
|
/// <param name="oldDisplayIndex">the old display index of the column</param>
|
|
/// <param name="newDisplayIndex">the new display index of the column</param>
|
|
private void OnColumnDisplayIndexChanged(EGC.DataGridColumn column, int oldDisplayIndex, int newDisplayIndex)
|
|
{
|
|
// Handle ClearValue. -1 is the default value and really means 'DisplayIndex should be the index of the column in the column collection'.
|
|
// We immediately replace the display index without notifying anyone.
|
|
if (oldDisplayIndex == -1 || _isClearingDisplayIndex)
|
|
{
|
|
// change from -1 to the new value; the OnColumnDisplayIndexChanged further down the stack (from old value to -1) will handle
|
|
// notifying the user and updating columns.
|
|
return;
|
|
}
|
|
|
|
// The DisplayIndex may have changed to the default value.
|
|
newDisplayIndex = CoerceDefaultDisplayIndex(column);
|
|
|
|
if (newDisplayIndex == oldDisplayIndex)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Our coerce value callback should have validated the DisplayIndex. Fire the virtual.
|
|
Debug.Assert(newDisplayIndex >= 0 && newDisplayIndex < Count, "The new DisplayIndex should have already been validated");
|
|
DataGridOwner.OnColumnDisplayIndexChanged(new EGC.DataGridColumnEventArgs(column));
|
|
|
|
// Call our helper to walk through all other columns and adjust their display indices.
|
|
UpdateDisplayIndexForChangedColumn(oldDisplayIndex, newDisplayIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the DisplayIndex for a single column has changed. The other columns may have conflicting display indices, so
|
|
/// we walk through them and adjust. This method does nothing if we're already updating display index as part of a larger
|
|
/// operation (such as add or remove). This is both for re-entrancy and to avoid modifying the display index map as we walk over
|
|
/// the columns.
|
|
/// </summary>
|
|
private void UpdateDisplayIndexForChangedColumn(int oldDisplayIndex, int newDisplayIndex)
|
|
{
|
|
// The code below adjusts the DisplayIndex of other columns and shouldn't happen if this column's display index is changed
|
|
// to account for the change in another.
|
|
if (IsUpdatingDisplayIndex)
|
|
{
|
|
// Avoid re-entrancy; setting DisplayIndex on columns causes their OnDisplayIndexChanged to fire.
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
IsUpdatingDisplayIndex = true;
|
|
|
|
Debug.Assert(oldDisplayIndex != newDisplayIndex, "A column's display index must have changed for us to call OnColumnDisplayIndexChanged");
|
|
Debug.Assert(oldDisplayIndex >= 0 && oldDisplayIndex < Count, "The old DisplayIndex should be valid");
|
|
|
|
// Update the display index mapping for all affected columns.
|
|
int columnIndex = DisplayIndexMap[oldDisplayIndex];
|
|
DisplayIndexMap.RemoveAt(oldDisplayIndex);
|
|
DisplayIndexMap.Insert(newDisplayIndex, columnIndex);
|
|
|
|
// Update the display index of other columns.
|
|
if (newDisplayIndex < oldDisplayIndex)
|
|
{
|
|
// DisplayIndex decreased. All columns with DisplayIndex >= newDisplayIndex and < oldDisplayIndex
|
|
// get their DisplayIndex incremented.
|
|
for (int i = newDisplayIndex + 1; i <= oldDisplayIndex; i++)
|
|
{
|
|
ColumnFromDisplayIndex(i).DisplayIndex++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// DisplayIndex increased. All columns with DisplayIndex <= newDisplayIndex and > oldDisplayIndex get their DisplayIndex decremented.
|
|
for (int i = oldDisplayIndex; i < newDisplayIndex; i++)
|
|
{
|
|
ColumnFromDisplayIndex(i).DisplayIndex--;
|
|
}
|
|
}
|
|
|
|
Debug_VerifyDisplayIndexMap();
|
|
|
|
DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Move, oldDisplayIndex, null, newDisplayIndex);
|
|
}
|
|
finally
|
|
{
|
|
IsUpdatingDisplayIndex = false;
|
|
}
|
|
}
|
|
|
|
private void UpdateDisplayIndexForMovedColumn(int oldColumnIndex, int newColumnIndex)
|
|
{
|
|
int displayIndex = RemoveFromDisplayIndexMap(oldColumnIndex);
|
|
InsertInDisplayIndexMap(displayIndex, newColumnIndex);
|
|
DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Move, oldColumnIndex, null, newColumnIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the DisplayIndex on all newly inserted or added columns and updates the existing columns as necessary.
|
|
/// </summary>
|
|
private void UpdateDisplayIndexForNewColumns(IList newColumns, int startingIndex)
|
|
{
|
|
EGC.DataGridColumn column;
|
|
int newDisplayIndex, columnIndex;
|
|
|
|
Debug.Assert(
|
|
newColumns.Count == 1,
|
|
"This derives from ObservableCollection; it is impossible to add multiple columns at once");
|
|
Debug.Assert(IsUpdatingDisplayIndex == false, "We don't add new columns as part of a display index update operation");
|
|
|
|
try
|
|
{
|
|
IsUpdatingDisplayIndex = true;
|
|
|
|
// Set the display index of the new columns and add them to the DisplayIndexMap
|
|
column = (EGC.DataGridColumn)newColumns[0];
|
|
columnIndex = startingIndex;
|
|
|
|
newDisplayIndex = CoerceDefaultDisplayIndex(column, columnIndex);
|
|
|
|
// Inserting the column in the map means that all columns with display index >= the new column's display index
|
|
// were given a higher display index. This is perfect, except that the column indices have changed due to the insert
|
|
// in the column collection. We need to iterate over the column indices and increment them appropriately. We also
|
|
// need to give each changed column a new display index.
|
|
InsertInDisplayIndexMap(newDisplayIndex, columnIndex);
|
|
|
|
for (int i = 0; i < DisplayIndexMap.Count; i++)
|
|
{
|
|
if (i > newDisplayIndex)
|
|
{
|
|
// All columns with DisplayIndex higher than the newly inserted columns
|
|
// need to have their DisplayIndex adiusted.
|
|
column = ColumnFromDisplayIndex(i);
|
|
column.DisplayIndex++;
|
|
}
|
|
}
|
|
|
|
Debug_VerifyDisplayIndexMap();
|
|
|
|
DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Add, -1, null, newDisplayIndex);
|
|
}
|
|
finally
|
|
{
|
|
IsUpdatingDisplayIndex = false;
|
|
}
|
|
}
|
|
|
|
// This method is called in first DataGrid measure call
|
|
// It needs to populate DisplayIndexMap and validate the DisplayIndex of all columns
|
|
internal void InitializeDisplayIndexMap()
|
|
{
|
|
if (_displayIndexMapInitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_displayIndexMapInitialized = true;
|
|
|
|
Debug.Assert(DisplayIndexMap.Count == 0, "DisplayIndexMap should be empty until first measure call.");
|
|
int columnCount = Count;
|
|
Dictionary<int, int> assignedDisplayIndexMap = new Dictionary<int, int>(); // <DisplayIndex, ColumnIndex>
|
|
|
|
// First loop:
|
|
// 1. Validate all columns DisplayIndex
|
|
// 2. Add columns with DisplayIndex!=default to the assignedDisplayIndexMap
|
|
for (int columnIndex = 0; columnIndex < columnCount; columnIndex++)
|
|
{
|
|
EGC.DataGridColumn currentColumn = this[columnIndex];
|
|
int currentColumnDisplayIndex = currentColumn.DisplayIndex;
|
|
|
|
ValidateDisplayIndex(currentColumn, currentColumnDisplayIndex);
|
|
if (currentColumnDisplayIndex >= 0)
|
|
{
|
|
if (assignedDisplayIndexMap.ContainsKey(currentColumnDisplayIndex))
|
|
{
|
|
throw new ArgumentException(SR.Get(SRID.DataGrid_DuplicateDisplayIndex));
|
|
}
|
|
|
|
assignedDisplayIndexMap.Add(currentColumnDisplayIndex, columnIndex);
|
|
}
|
|
}
|
|
|
|
// Second loop:
|
|
// Assign DisplayIndex to the columns with default values
|
|
int nextAvailableColumnIndex = 0;
|
|
for (int columnIndex = 0; columnIndex < columnCount; columnIndex++)
|
|
{
|
|
EGC.DataGridColumn currentColumn = this[columnIndex];
|
|
int currentColumnDisplayIndex = currentColumn.DisplayIndex;
|
|
|
|
bool hasDefaultDisplayIndex = EGC.DataGridHelper.IsDefaultValue(currentColumn, EGC.DataGridColumn.DisplayIndexProperty);
|
|
if (hasDefaultDisplayIndex)
|
|
{
|
|
while (assignedDisplayIndexMap.ContainsKey(nextAvailableColumnIndex))
|
|
{
|
|
nextAvailableColumnIndex++;
|
|
}
|
|
|
|
CoerceDefaultDisplayIndex(currentColumn, nextAvailableColumnIndex);
|
|
assignedDisplayIndexMap.Add(nextAvailableColumnIndex, columnIndex);
|
|
nextAvailableColumnIndex++;
|
|
}
|
|
}
|
|
|
|
// Third loop:
|
|
// Copy generated assignedDisplayIndexMap into DisplayIndexMap
|
|
for (int displayIndex = 0; displayIndex < columnCount; displayIndex++)
|
|
{
|
|
Debug.Assert(assignedDisplayIndexMap.ContainsKey(displayIndex));
|
|
DisplayIndexMap.Add(assignedDisplayIndexMap[displayIndex]);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the display index for all columns affected by the removal of a set of columns.
|
|
/// </summary>
|
|
private void UpdateDisplayIndexForRemovedColumns(IList oldColumns, int startingIndex)
|
|
{
|
|
EGC.DataGridColumn column;
|
|
Debug.Assert(
|
|
oldColumns.Count == 1,
|
|
"This derives from ObservableCollection; it is impossible to remove multiple columns at once");
|
|
Debug.Assert(IsUpdatingDisplayIndex == false, "We don't remove columns as part of a display index update operation");
|
|
|
|
try
|
|
{
|
|
IsUpdatingDisplayIndex = true;
|
|
Debug.Assert(DisplayIndexMap.Count > Count, "Columns were just removed: the display index map shouldn't have yet been updated");
|
|
|
|
int removedDisplayIndex = RemoveFromDisplayIndexMap(startingIndex);
|
|
|
|
// Removing the column in the map means that all columns with display index >= the new column's display index
|
|
// were given a lower display index. This is perfect, except that the column indices have changed due to the insert
|
|
// in the column collection. We need to iterate over the column indices and decrement them appropriately. We also
|
|
// need to give each changed column a new display index.
|
|
for (int i = 0; i < DisplayIndexMap.Count; i++)
|
|
{
|
|
if (i >= removedDisplayIndex)
|
|
{
|
|
// All columns with DisplayIndex higher than the newly deleted columns need to have their DisplayIndex adiusted
|
|
// (we use >= because a column will have been decremented to have the same display index as the deleted column).
|
|
column = ColumnFromDisplayIndex(i);
|
|
column.DisplayIndex--;
|
|
}
|
|
}
|
|
|
|
Debug_VerifyDisplayIndexMap();
|
|
|
|
DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Remove, removedDisplayIndex, (EGC.DataGridColumn)oldColumns[0], -1);
|
|
}
|
|
finally
|
|
{
|
|
IsUpdatingDisplayIndex = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the display index for the column that was just replaced and adjusts the other columns if necessary
|
|
/// </summary>
|
|
private void UpdateDisplayIndexForReplacedColumn(IList oldColumns, IList newColumns)
|
|
{
|
|
if (oldColumns != null && oldColumns.Count > 0 && newColumns != null && newColumns.Count > 0)
|
|
{
|
|
Debug.Assert(oldColumns.Count == 1 && newColumns.Count == 1, "Multi replace isn't possible with ObservableCollection");
|
|
EGC.DataGridColumn oldColumn = (EGC.DataGridColumn)oldColumns[0];
|
|
EGC.DataGridColumn newColumn = (EGC.DataGridColumn)newColumns[0];
|
|
|
|
if (oldColumn != null && newColumn != null)
|
|
{
|
|
int newDisplayIndex = CoerceDefaultDisplayIndex(newColumn);
|
|
|
|
if (oldColumn.DisplayIndex != newDisplayIndex)
|
|
{
|
|
// Update the display index of other columns to adjust for that of the new one.
|
|
UpdateDisplayIndexForChangedColumn(oldColumn.DisplayIndex, newDisplayIndex);
|
|
}
|
|
|
|
DataGridOwner.UpdateColumnsOnVirtualizedCellInfoCollections(NotifyCollectionChangedAction.Replace, newDisplayIndex, oldColumn, newDisplayIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the DisplayIndexProperty on each of the columns.
|
|
/// </summary>
|
|
private void ClearDisplayIndex(IList oldColumns, IList newColumns)
|
|
{
|
|
if (oldColumns != null)
|
|
{
|
|
try
|
|
{
|
|
_isClearingDisplayIndex = true;
|
|
var count = oldColumns.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var column = (EGC.DataGridColumn)oldColumns[i];
|
|
|
|
// Only clear the old column's index if its not in newColumns
|
|
if (newColumns != null && newColumns.Contains(column))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
column.ClearValue(EGC.DataGridColumn.DisplayIndexProperty);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_isClearingDisplayIndex = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the display index is valid for the given column
|
|
/// </summary>
|
|
private bool IsDisplayIndexValid(EGC.DataGridColumn column, int displayIndex, bool isAdding)
|
|
{
|
|
// -1 is legal only as a default value
|
|
if (displayIndex == -1 && EGC.DataGridHelper.IsDefaultValue(column, EGC.DataGridColumn.DisplayIndexProperty))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// If we're adding a column the count will soon be increased by one -- so a DisplayIndex == Count is ok.
|
|
return displayIndex >= 0 && (isAdding ? displayIndex <= Count : displayIndex < Count);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts the given columnIndex in the DisplayIndexMap at the given display index.
|
|
/// </summary>
|
|
private void InsertInDisplayIndexMap(int newDisplayIndex, int columnIndex)
|
|
{
|
|
DisplayIndexMap.Insert(newDisplayIndex, columnIndex);
|
|
|
|
for (int i = 0; i < DisplayIndexMap.Count; i++)
|
|
{
|
|
if (DisplayIndexMap[i] >= columnIndex && i != newDisplayIndex)
|
|
{
|
|
// These are columns that are after the inserted item in the column collection; we have to adiust
|
|
// to account for the shifted column index.
|
|
DisplayIndexMap[i]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the given column index from the DisplayIndexMap
|
|
/// </summary>
|
|
private int RemoveFromDisplayIndexMap(int columnIndex)
|
|
{
|
|
int removedDisplayIndex = DisplayIndexMap.IndexOf(columnIndex);
|
|
Debug.Assert(removedDisplayIndex >= 0);
|
|
|
|
DisplayIndexMap.RemoveAt(removedDisplayIndex);
|
|
|
|
for (int i = 0; i < DisplayIndexMap.Count; i++)
|
|
{
|
|
if (DisplayIndexMap[i] >= columnIndex)
|
|
{
|
|
// These are columns that are after the removed item in the column collection; we have to adiust
|
|
// to account for the shifted column index.
|
|
DisplayIndexMap[i]--;
|
|
}
|
|
}
|
|
|
|
return removedDisplayIndex;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Throws an ArgumentOutOfRangeException if the given displayIndex is invalid for the given column.
|
|
/// </summary>
|
|
internal void ValidateDisplayIndex(EGC.DataGridColumn column, int displayIndex)
|
|
{
|
|
ValidateDisplayIndex(column, displayIndex, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Throws an ArgumentOutOfRangeException if the given displayIndex is invalid for the given column.
|
|
/// </summary>
|
|
internal void ValidateDisplayIndex(EGC.DataGridColumn column, int displayIndex, bool isAdding)
|
|
{
|
|
if (!IsDisplayIndexValid(column, displayIndex, isAdding))
|
|
{
|
|
throw new ArgumentOutOfRangeException("displayIndex", displayIndex, SR.Get(SRID.DataGrid_ColumnDisplayIndexOutOfRange, column.Header));
|
|
}
|
|
}
|
|
|
|
[Conditional("DEBUG")]
|
|
private void Debug_VerifyDisplayIndexMap()
|
|
{
|
|
Debug.Assert(Count == DisplayIndexMap.Count, "Display Index map is of the wrong size");
|
|
for (int i = 0; i < DisplayIndexMap.Count; i++)
|
|
{
|
|
Debug.Assert(DisplayIndexMap[i] >= 0 && DisplayIndexMap[i] < Count, "DisplayIndex map entry doesn't point to a valid column");
|
|
Debug.Assert(ColumnFromDisplayIndex(i).DisplayIndex == i, "DisplayIndex map doesn't match column indices");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Frozen Columns
|
|
|
|
/// <summary>
|
|
/// Method which sets / resets the IsFrozen property of columns based on DataGrid's FrozenColumnCount.
|
|
/// It is possible that the FrozenColumnCount change could be a result of column count itself, in
|
|
/// which case only the columns which are in the collection at the moment are to be considered.
|
|
/// </summary>
|
|
/// <param name="oldFrozenCount"></param>
|
|
/// <param name="newFrozenCount"></param>
|
|
private void OnDataGridFrozenColumnCountChanged(int oldFrozenCount, int newFrozenCount)
|
|
{
|
|
if (newFrozenCount > oldFrozenCount)
|
|
{
|
|
int columnCount = Math.Min(newFrozenCount, Count);
|
|
for (int i = oldFrozenCount; i < columnCount; i++)
|
|
{
|
|
ColumnFromDisplayIndex(i).IsFrozen = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int columnCount = Math.Min(oldFrozenCount, Count);
|
|
for (int i = newFrozenCount; i < columnCount; i++)
|
|
{
|
|
ColumnFromDisplayIndex(i).IsFrozen = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helpers
|
|
|
|
private EGC.DataGrid DataGridOwner
|
|
{
|
|
get { return _dataGridOwner; }
|
|
}
|
|
|
|
// Used by DataGridColumnCollection to delay the validation of DisplayIndex
|
|
// Validation should be delayed because we in the process of adding columns we may have DisplayIndex less that current columns number
|
|
// After all columns are generated or added in xaml we can do the validation
|
|
internal bool DisplayIndexMapInitialized
|
|
{
|
|
get
|
|
{
|
|
return _displayIndexMapInitialized;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Star Column Helper
|
|
|
|
/// <summary>
|
|
/// Method which determines if there are any
|
|
/// star columns in datagrid except the given column and also returns perStarWidth
|
|
/// </summary>
|
|
private bool HasVisibleStarColumnsInternal(EGC.DataGridColumn ignoredColumn, out double perStarWidth)
|
|
{
|
|
bool hasStarColumns = false;
|
|
perStarWidth = 0.0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (column == ignoredColumn ||
|
|
!column.IsVisible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
if (width.IsStar)
|
|
{
|
|
hasStarColumns = true;
|
|
if (!DoubleUtil.AreClose(width.Value, 0.0) &&
|
|
!DoubleUtil.AreClose(width.DesiredValue, 0.0))
|
|
{
|
|
perStarWidth = width.DesiredValue / width.Value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hasStarColumns;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which determines if there are any
|
|
/// star columns in datagrid and also returns perStarWidth
|
|
/// </summary>
|
|
private bool HasVisibleStarColumnsInternal(out double perStarWidth)
|
|
{
|
|
return HasVisibleStarColumnsInternal(null, out perStarWidth);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which determines if there are any
|
|
/// star columns in datagrid except the given column
|
|
/// </summary>
|
|
private bool HasVisibleStarColumnsInternal(EGC.DataGridColumn ignoredColumn)
|
|
{
|
|
double perStarWidth;
|
|
return HasVisibleStarColumnsInternal(ignoredColumn, out perStarWidth);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property which determines if there are any star columns
|
|
/// in the datagrid.
|
|
/// </summary>
|
|
internal bool HasVisibleStarColumns
|
|
{
|
|
get; private set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which redetermines if the collection has any star columns are not.
|
|
/// </summary>
|
|
internal void InvalidateHasVisibleStarColumns()
|
|
{
|
|
HasVisibleStarColumns = HasVisibleStarColumnsInternal(null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which redistributes the width of star columns among them selves
|
|
/// </summary>
|
|
private void RecomputeStarColumnWidths()
|
|
{
|
|
double totalDisplaySpace = DataGridOwner.GetViewportWidthForColumns();
|
|
double nonStarSpace = 0.0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
EGC.DataGridLength width = column.Width;
|
|
if (column.IsVisible && !width.IsStar)
|
|
{
|
|
nonStarSpace += width.DisplayValue;
|
|
}
|
|
}
|
|
|
|
if (DoubleUtil.IsNaN(nonStarSpace))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ComputeStarColumnWidths(totalDisplaySpace - nonStarSpace);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which computes the widths of all the star columns
|
|
/// </summary>
|
|
private double ComputeStarColumnWidths(double availableStarSpace)
|
|
{
|
|
Debug.Assert(
|
|
!DoubleUtil.IsNaN(availableStarSpace) && !Double.IsNegativeInfinity(availableStarSpace) && !Double.IsPositiveInfinity(availableStarSpace),
|
|
"availableStarSpace is not valid");
|
|
|
|
List<EGC.DataGridColumn> unResolvedColumns = new List<EGC.DataGridColumn>();
|
|
List<EGC.DataGridColumn> partialResolvedColumns = new List<EGC.DataGridColumn>();
|
|
double totalFactors = 0.0;
|
|
double totalMinWidths = 0.0;
|
|
double totalMaxWidths = 0.0;
|
|
double utilizedStarSpace = 0.0;
|
|
|
|
// Accumulate all the star columns into unResolvedColumns in the beginning
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
EGC.DataGridLength width = column.Width;
|
|
if (column.IsVisible && width.IsStar)
|
|
{
|
|
unResolvedColumns.Add(column);
|
|
totalFactors += width.Value;
|
|
totalMinWidths += column.MinWidth;
|
|
totalMaxWidths += column.MaxWidth;
|
|
}
|
|
}
|
|
|
|
if (DoubleUtil.LessThan(availableStarSpace, totalMinWidths))
|
|
{
|
|
availableStarSpace = totalMinWidths;
|
|
}
|
|
|
|
if (DoubleUtil.GreaterThan(availableStarSpace, totalMaxWidths))
|
|
{
|
|
availableStarSpace = totalMaxWidths;
|
|
}
|
|
|
|
while (unResolvedColumns.Count > 0)
|
|
{
|
|
double starValue = availableStarSpace / totalFactors;
|
|
|
|
// Find all the columns whose star share is less than thier min width and move such columns
|
|
// into partialResolvedColumns giving them atleast the minwidth and there by reducing the availableSpace and totalFactors
|
|
for (int i = 0, count = unResolvedColumns.Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = unResolvedColumns[i];
|
|
EGC.DataGridLength width = column.Width;
|
|
|
|
double columnMinWidth = column.MinWidth;
|
|
double starColumnWidth = availableStarSpace * width.Value / totalFactors;
|
|
|
|
if (DoubleUtil.GreaterThan(columnMinWidth, starColumnWidth))
|
|
{
|
|
availableStarSpace = Math.Max(0.0, availableStarSpace - columnMinWidth);
|
|
totalFactors -= width.Value;
|
|
unResolvedColumns.RemoveAt(i);
|
|
i--;
|
|
count--;
|
|
partialResolvedColumns.Add(column);
|
|
}
|
|
}
|
|
|
|
// With the remaining space determine in any columns star share is more than maxwidth.
|
|
// If such columns are found give them their max width and remove them from unResolvedColumns
|
|
// there by reducing the availablespace and totalfactors. If such column is found, the remaining columns are to be recomputed
|
|
bool iterationRequired = false;
|
|
for (int i = 0, count = unResolvedColumns.Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = unResolvedColumns[i];
|
|
EGC.DataGridLength width = column.Width;
|
|
|
|
double columnMaxWidth = column.MaxWidth;
|
|
double starColumnWidth = availableStarSpace * width.Value / totalFactors;
|
|
|
|
if (DoubleUtil.LessThan(columnMaxWidth, starColumnWidth))
|
|
{
|
|
iterationRequired = true;
|
|
unResolvedColumns.RemoveAt(i);
|
|
availableStarSpace -= columnMaxWidth;
|
|
utilizedStarSpace += columnMaxWidth;
|
|
totalFactors -= width.Value;
|
|
column.UpdateWidthForStarColumn(columnMaxWidth, starValue * width.Value, width.Value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If it was determined by the previous step that another iteration is needed
|
|
// then move all the partialResolvedColumns back to unResolvedColumns and there by
|
|
// restoring availablespace and totalfactors.
|
|
// If another iteration is not needed then allocate min widths to all columns in
|
|
// partial resolved columns and star share to all unresolved columns there by
|
|
// ending the loop
|
|
if (iterationRequired)
|
|
{
|
|
for (int i = 0, count = partialResolvedColumns.Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = partialResolvedColumns[i];
|
|
|
|
unResolvedColumns.Add(column);
|
|
availableStarSpace += column.MinWidth;
|
|
totalFactors += column.Width.Value;
|
|
}
|
|
|
|
partialResolvedColumns.Clear();
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0, count = partialResolvedColumns.Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = partialResolvedColumns[i];
|
|
EGC.DataGridLength width = column.Width;
|
|
double columnMinWidth = column.MinWidth;
|
|
column.UpdateWidthForStarColumn(columnMinWidth, width.Value * starValue, width.Value);
|
|
utilizedStarSpace += columnMinWidth;
|
|
}
|
|
|
|
partialResolvedColumns.Clear();
|
|
for (int i = 0, count = unResolvedColumns.Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = unResolvedColumns[i];
|
|
EGC.DataGridLength width = column.Width;
|
|
double starColumnWidth = availableStarSpace * width.Value / totalFactors;
|
|
column.UpdateWidthForStarColumn(starColumnWidth, width.Value * starValue, width.Value);
|
|
utilizedStarSpace += starColumnWidth;
|
|
}
|
|
|
|
unResolvedColumns.Clear();
|
|
}
|
|
}
|
|
|
|
return utilizedStarSpace;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Column Width Computation Helper
|
|
|
|
/// <summary>
|
|
/// Method which handles the column widths computation for CellsPanelHorizontalOffset change
|
|
/// </summary>
|
|
private void OnCellsPanelHorizontalOffsetChanged(DependencyPropertyChangedEventArgs e)
|
|
{
|
|
InvalidateColumnRealization(false);
|
|
|
|
// Change in CellsPanelOffset width has an opposite effect on Column
|
|
// width distribution. Hence widthChange is (oldvalue - newvalue)
|
|
double totalAvailableWidth = DataGridOwner.GetViewportWidthForColumns();
|
|
RedistributeColumnWidthsOnAvailableSpaceChange((double)e.OldValue - (double)e.NewValue, totalAvailableWidth);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to invalidate the average width computation
|
|
/// </summary>
|
|
internal void InvalidateAverageColumnWidth()
|
|
{
|
|
_averageColumnWidth = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property holding the average widht of columns
|
|
/// </summary>
|
|
internal double AverageColumnWidth
|
|
{
|
|
get
|
|
{
|
|
if (!_averageColumnWidth.HasValue)
|
|
{
|
|
_averageColumnWidth = ComputeAverageColumnWidth();
|
|
}
|
|
|
|
return _averageColumnWidth.Value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which determines the average with of all the columns
|
|
/// </summary>
|
|
private double ComputeAverageColumnWidth()
|
|
{
|
|
double eligibleDisplayValue = 0.0;
|
|
int totalFactors = 0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
EGC.DataGridLength width = column.Width;
|
|
if (column.IsVisible && !DoubleUtil.IsNaN(width.DisplayValue))
|
|
{
|
|
eligibleDisplayValue += width.DisplayValue;
|
|
totalFactors++;
|
|
}
|
|
}
|
|
|
|
if (totalFactors != 0)
|
|
{
|
|
return eligibleDisplayValue / totalFactors;
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property indicating whether the column width computation opertaion is pending
|
|
/// </summary>
|
|
internal bool ColumnWidthsComputationPending
|
|
{
|
|
get
|
|
{
|
|
return _columnWidthsComputationPending;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to invalidate the column width computation
|
|
/// </summary>
|
|
internal void InvalidateColumnWidthsComputation()
|
|
{
|
|
if (_columnWidthsComputationPending)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DataGridOwner.Dispatcher.BeginInvoke(new DispatcherOperationCallback(ComputeColumnWidths), DispatcherPriority.Render, this);
|
|
_columnWidthsComputationPending = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which computes the widths of the columns. Used as a callback
|
|
/// to dispatcher operation
|
|
/// </summary>
|
|
private object ComputeColumnWidths(object arg)
|
|
{
|
|
ComputeColumnWidths();
|
|
DataGridOwner.NotifyPropertyChanged(
|
|
DataGridOwner,
|
|
"DelayedColumnWidthComputation",
|
|
new DependencyPropertyChangedEventArgs(),
|
|
NotificationTarget.CellsPresenter | NotificationTarget.ColumnHeadersPresenter);
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which computes the widths of the columns
|
|
/// </summary>
|
|
private void ComputeColumnWidths()
|
|
{
|
|
if (HasVisibleStarColumns)
|
|
{
|
|
InitializeColumnDisplayValues();
|
|
DistributeSpaceAmongColumns(DataGridOwner.GetViewportWidthForColumns());
|
|
}
|
|
else
|
|
{
|
|
ExpandAllColumnWidthsToDesiredValue();
|
|
}
|
|
|
|
_columnWidthsComputationPending = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which initializes the column width's diplay value to its desired value
|
|
/// </summary>
|
|
private void InitializeColumnDisplayValues()
|
|
{
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (!column.IsVisible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
if (!width.IsStar)
|
|
{
|
|
double minWidth = column.MinWidth;
|
|
double displayValue = EGC.DataGridHelper.CoerceToMinMax(DoubleUtil.IsNaN(width.DesiredValue) ? minWidth : width.DesiredValue, minWidth, column.MaxWidth);
|
|
if (!DoubleUtil.AreClose(width.DisplayValue, displayValue))
|
|
{
|
|
column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, displayValue));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which redistributes the column widths based on change in MinWidth of a column
|
|
/// </summary>
|
|
internal void RedistributeColumnWidthsOnMinWidthChangeOfColumn(EGC.DataGridColumn changedColumn, double oldMinWidth)
|
|
{
|
|
EGC.DataGridLength width = changedColumn.Width;
|
|
double minWidth = changedColumn.MinWidth;
|
|
if (DoubleUtil.GreaterThan(minWidth, width.DisplayValue))
|
|
{
|
|
if (HasVisibleStarColumns)
|
|
{
|
|
TakeAwayWidthFromColumns(changedColumn, minWidth - width.DisplayValue, false);
|
|
}
|
|
|
|
changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, minWidth));
|
|
}
|
|
else if (DoubleUtil.LessThan(minWidth, oldMinWidth))
|
|
{
|
|
if (width.IsStar)
|
|
{
|
|
if (DoubleUtil.AreClose(width.DisplayValue, oldMinWidth))
|
|
{
|
|
GiveAwayWidthToColumns(changedColumn, oldMinWidth - minWidth, true);
|
|
}
|
|
}
|
|
else if (DoubleUtil.GreaterThan(oldMinWidth, width.DesiredValue))
|
|
{
|
|
double displayValue = Math.Max(width.DesiredValue, minWidth);
|
|
if (HasVisibleStarColumns)
|
|
{
|
|
GiveAwayWidthToColumns(changedColumn, oldMinWidth - displayValue);
|
|
}
|
|
|
|
changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, displayValue));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which redistributes the column widths based on change in MaxWidth of a column
|
|
/// </summary>
|
|
internal void RedistributeColumnWidthsOnMaxWidthChangeOfColumn(EGC.DataGridColumn changedColumn, double oldMaxWidth)
|
|
{
|
|
EGC.DataGridLength width = changedColumn.Width;
|
|
double maxWidth = changedColumn.MaxWidth;
|
|
if (DoubleUtil.LessThan(maxWidth, width.DisplayValue))
|
|
{
|
|
if (HasVisibleStarColumns)
|
|
{
|
|
GiveAwayWidthToColumns(changedColumn, width.DisplayValue - maxWidth);
|
|
}
|
|
|
|
changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, maxWidth));
|
|
}
|
|
else if (DoubleUtil.GreaterThan(maxWidth, oldMaxWidth))
|
|
{
|
|
if (width.IsStar)
|
|
{
|
|
RecomputeStarColumnWidths();
|
|
}
|
|
else if (DoubleUtil.LessThan(oldMaxWidth, width.DesiredValue))
|
|
{
|
|
double displayValue = Math.Min(width.DesiredValue, maxWidth);
|
|
if (HasVisibleStarColumns)
|
|
{
|
|
double leftOverSpace = TakeAwayWidthFromUnusedSpace(false, displayValue - oldMaxWidth);
|
|
leftOverSpace = TakeAwayWidthFromStarColumns(changedColumn, leftOverSpace);
|
|
displayValue -= leftOverSpace;
|
|
}
|
|
|
|
changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, displayValue));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which redistributes the column widths based on change in Width of a column
|
|
/// </summary>
|
|
internal void RedistributeColumnWidthsOnWidthChangeOfColumn(EGC.DataGridColumn changedColumn, EGC.DataGridLength oldWidth)
|
|
{
|
|
EGC.DataGridLength width = changedColumn.Width;
|
|
bool hasStarColumns = HasVisibleStarColumns;
|
|
if (oldWidth.IsStar && !width.IsStar && !hasStarColumns)
|
|
{
|
|
ExpandAllColumnWidthsToDesiredValue();
|
|
}
|
|
else if (width.IsStar && !oldWidth.IsStar)
|
|
{
|
|
if (!HasVisibleStarColumnsInternal(changedColumn))
|
|
{
|
|
ComputeColumnWidths();
|
|
}
|
|
else
|
|
{
|
|
double minWidth = changedColumn.MinWidth;
|
|
double leftOverSpace = GiveAwayWidthToNonStarColumns(null, oldWidth.DisplayValue - minWidth);
|
|
changedColumn.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, minWidth + leftOverSpace));
|
|
RecomputeStarColumnWidths();
|
|
}
|
|
}
|
|
else if (width.IsStar && oldWidth.IsStar)
|
|
{
|
|
RecomputeStarColumnWidths();
|
|
}
|
|
else if (hasStarColumns)
|
|
{
|
|
RedistributeColumnWidthsOnNonStarWidthChange(
|
|
changedColumn,
|
|
oldWidth);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which redistributes the column widths based on change in available space of a column
|
|
/// </summary>
|
|
internal void RedistributeColumnWidthsOnAvailableSpaceChange(double availableSpaceChange, double newTotalAvailableSpace)
|
|
{
|
|
if (!ColumnWidthsComputationPending && HasVisibleStarColumns)
|
|
{
|
|
if (DoubleUtil.GreaterThan(availableSpaceChange, 0.0))
|
|
{
|
|
GiveAwayWidthToColumns(null, availableSpaceChange);
|
|
}
|
|
else if (DoubleUtil.LessThan(availableSpaceChange, 0.0))
|
|
{
|
|
TakeAwayWidthFromColumns(null, Math.Abs(availableSpaceChange), false, newTotalAvailableSpace);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which expands the display values of widths of all columns to
|
|
/// their desired values. Usually used when the last star column's width
|
|
/// is changed to non-star
|
|
/// </summary>
|
|
private void ExpandAllColumnWidthsToDesiredValue()
|
|
{
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (!column.IsVisible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
double maxWidth = column.MaxWidth;
|
|
if (DoubleUtil.GreaterThan(width.DesiredValue, width.DisplayValue) &&
|
|
!DoubleUtil.AreClose(width.DisplayValue, maxWidth))
|
|
{
|
|
column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, Math.Min(width.DesiredValue, maxWidth)));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which redistributes widths of columns on change of a column's width
|
|
/// when datagrid itself has star columns, but neither the oldwidth or the newwidth
|
|
/// of changed column is star.
|
|
/// </summary>
|
|
private void RedistributeColumnWidthsOnNonStarWidthChange(EGC.DataGridColumn changedColumn, EGC.DataGridLength oldWidth)
|
|
{
|
|
EGC.DataGridLength width = changedColumn.Width;
|
|
if (DoubleUtil.GreaterThan(width.DesiredValue, oldWidth.DisplayValue))
|
|
{
|
|
double nonRetrievableSpace = TakeAwayWidthFromColumns(changedColumn, width.DesiredValue - oldWidth.DisplayValue, changedColumn != null);
|
|
if (DoubleUtil.GreaterThan(nonRetrievableSpace, 0.0))
|
|
{
|
|
changedColumn.SetWidthInternal(new EGC.DataGridLength(
|
|
width.Value,
|
|
width.UnitType,
|
|
width.DesiredValue,
|
|
Math.Max(width.DisplayValue - nonRetrievableSpace, changedColumn.MinWidth)));
|
|
}
|
|
}
|
|
else if (DoubleUtil.LessThan(width.DesiredValue, oldWidth.DisplayValue))
|
|
{
|
|
double newDesiredValue = EGC.DataGridHelper.CoerceToMinMax(width.DesiredValue, changedColumn.MinWidth, changedColumn.MaxWidth);
|
|
GiveAwayWidthToColumns(changedColumn, oldWidth.DisplayValue - newDesiredValue);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which distributes a given amount of width among all the columns
|
|
/// </summary>
|
|
private void DistributeSpaceAmongColumns(double availableSpace)
|
|
{
|
|
double sumOfMinWidths = 0.0;
|
|
double sumOfMaxWidths = 0.0;
|
|
double sumOfStarMinWidths = 0.0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (!column.IsVisible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sumOfMinWidths += column.MinWidth;
|
|
sumOfMaxWidths += column.MaxWidth;
|
|
if (column.Width.IsStar)
|
|
{
|
|
sumOfStarMinWidths += column.MinWidth;
|
|
}
|
|
}
|
|
|
|
if (DoubleUtil.LessThan(availableSpace, sumOfMinWidths))
|
|
{
|
|
availableSpace = sumOfMinWidths;
|
|
}
|
|
|
|
if (DoubleUtil.GreaterThan(availableSpace, sumOfMaxWidths))
|
|
{
|
|
availableSpace = sumOfMaxWidths;
|
|
}
|
|
|
|
double nonStarSpaceLeftOver = DistributeSpaceAmongNonStarColumns(availableSpace - sumOfStarMinWidths);
|
|
|
|
ComputeStarColumnWidths(sumOfStarMinWidths + nonStarSpaceLeftOver);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which distributes a given amount of width among all non star columns
|
|
/// </summary>
|
|
private double DistributeSpaceAmongNonStarColumns(double availableSpace)
|
|
{
|
|
double requiredSpace = 0.0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
EGC.DataGridLength width = column.Width;
|
|
if (!column.IsVisible ||
|
|
width.IsStar)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
requiredSpace += width.DisplayValue;
|
|
}
|
|
|
|
if (DoubleUtil.LessThan(availableSpace, requiredSpace))
|
|
{
|
|
double spaceDeficit = requiredSpace - availableSpace;
|
|
TakeAwayWidthFromNonStarColumns(null, spaceDeficit);
|
|
}
|
|
|
|
return Math.Max(availableSpace - requiredSpace, 0.0);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Column Resizing Helper
|
|
|
|
/// <summary>
|
|
/// Method which is called when user resize of column starts
|
|
/// </summary>
|
|
internal void OnColumnResizeStarted()
|
|
{
|
|
_originalWidthsForResize = new Dictionary<EGC.DataGridColumn, EGC.DataGridLength>();
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
_originalWidthsForResize[column] = column.Width;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which is called when user resize of column ends
|
|
/// </summary>
|
|
internal void OnColumnResizeCompleted(bool cancel)
|
|
{
|
|
if (cancel && _originalWidthsForResize != null)
|
|
{
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (_originalWidthsForResize.ContainsKey(column))
|
|
{
|
|
column.Width = _originalWidthsForResize[column];
|
|
}
|
|
}
|
|
}
|
|
|
|
_originalWidthsForResize = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which recomputes the widths of columns on resize of column
|
|
/// </summary>
|
|
internal void RecomputeColumnWidthsOnColumnResize(EGC.DataGridColumn resizingColumn, double horizontalChange, bool retainAuto)
|
|
{
|
|
EGC.DataGridLength resizingColumnWidth = resizingColumn.Width;
|
|
double expectedRezingColumnWidth = resizingColumnWidth.DisplayValue + horizontalChange;
|
|
|
|
if (DoubleUtil.LessThan(expectedRezingColumnWidth, resizingColumn.MinWidth))
|
|
{
|
|
horizontalChange = resizingColumn.MinWidth - resizingColumnWidth.DisplayValue;
|
|
}
|
|
else if (DoubleUtil.GreaterThan(expectedRezingColumnWidth, resizingColumn.MaxWidth))
|
|
{
|
|
horizontalChange = resizingColumn.MaxWidth - resizingColumnWidth.DisplayValue;
|
|
}
|
|
|
|
int resizingColumnIndex = resizingColumn.DisplayIndex;
|
|
|
|
if (DoubleUtil.GreaterThan(horizontalChange, 0.0))
|
|
{
|
|
RecomputeColumnWidthsOnColumnPositiveResize(horizontalChange, resizingColumnIndex, retainAuto);
|
|
}
|
|
else if (DoubleUtil.LessThan(horizontalChange, 0.0))
|
|
{
|
|
RecomputeColumnWidthsOnColumnNegativeResize(-horizontalChange, resizingColumnIndex, retainAuto);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which computes widths of columns on positive resize of a column
|
|
/// </summary>
|
|
private void RecomputeColumnWidthsOnColumnPositiveResize(
|
|
double horizontalChange,
|
|
int resizingColumnIndex,
|
|
bool retainAuto)
|
|
{
|
|
double perStarWidth = 0.0;
|
|
if (HasVisibleStarColumnsInternal(out perStarWidth))
|
|
{
|
|
// reuse unused space
|
|
horizontalChange = TakeAwayUnusedSpaceOnColumnPositiveResize(horizontalChange, resizingColumnIndex, retainAuto);
|
|
|
|
// reducing star columns to right
|
|
horizontalChange = RecomputeStarColumnWidthsOnColumnPositiveResize(horizontalChange, resizingColumnIndex, perStarWidth, retainAuto);
|
|
|
|
// reducing columns to the right which are greater than the min size
|
|
horizontalChange = RecomputeNonStarColumnWidthsOnColumnPositiveResize(horizontalChange, resizingColumnIndex, retainAuto);
|
|
}
|
|
else
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(resizingColumnIndex);
|
|
SetResizedColumnWidth(column, horizontalChange, retainAuto);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which resizes the widths of star columns on positive resize of a column
|
|
/// </summary>
|
|
private double RecomputeStarColumnWidthsOnColumnPositiveResize(
|
|
double horizontalChange,
|
|
int resizingColumnIndex,
|
|
double perStarWidth,
|
|
bool retainAuto)
|
|
{
|
|
while (DoubleUtil.GreaterThan(horizontalChange, 0.0))
|
|
{
|
|
double minPerStarExcessRatio = Double.PositiveInfinity;
|
|
double rightStarFactors = GetStarFactorsForPositiveResize(resizingColumnIndex + 1, out minPerStarExcessRatio);
|
|
|
|
if (DoubleUtil.GreaterThan(rightStarFactors, 0.0))
|
|
{
|
|
horizontalChange = ReallocateStarValuesForPositiveResize(
|
|
resizingColumnIndex,
|
|
horizontalChange,
|
|
minPerStarExcessRatio,
|
|
rightStarFactors,
|
|
perStarWidth,
|
|
retainAuto);
|
|
|
|
if (DoubleUtil.AreClose(horizontalChange, 0.0))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return horizontalChange;
|
|
}
|
|
|
|
private static bool CanColumnParticipateInResize(EGC.DataGridColumn column)
|
|
{
|
|
return column.IsVisible && column.CanUserResize;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which returns the total of star factors of the columns which could be resized on positive resize of a column
|
|
/// </summary>
|
|
private double GetStarFactorsForPositiveResize(int startIndex, out double minPerStarExcessRatio)
|
|
{
|
|
minPerStarExcessRatio = Double.PositiveInfinity;
|
|
double rightStarFactors = 0.0;
|
|
for (int i = startIndex, count = Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
|
|
if (!CanColumnParticipateInResize(column))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
if (width.IsStar && !DoubleUtil.AreClose(width.Value, 0.0))
|
|
{
|
|
if (DoubleUtil.GreaterThan(width.DisplayValue, column.MinWidth))
|
|
{
|
|
rightStarFactors += width.Value;
|
|
double excessRatio = (width.DisplayValue - column.MinWidth) / width.Value;
|
|
if (DoubleUtil.LessThan(excessRatio, minPerStarExcessRatio))
|
|
{
|
|
minPerStarExcessRatio = excessRatio;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rightStarFactors;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which reallocated the star factors of star columns on
|
|
/// positive resize of a column
|
|
/// </summary>
|
|
private double ReallocateStarValuesForPositiveResize(
|
|
int startIndex,
|
|
double horizontalChange,
|
|
double perStarExcessRatio,
|
|
double totalStarFactors,
|
|
double perStarWidth,
|
|
bool retainAuto)
|
|
{
|
|
double changePerStar = 0.0;
|
|
double horizontalChangeForIteration = 0.0;
|
|
if (DoubleUtil.LessThan(horizontalChange, perStarExcessRatio * totalStarFactors))
|
|
{
|
|
changePerStar = horizontalChange / totalStarFactors;
|
|
horizontalChangeForIteration = horizontalChange;
|
|
horizontalChange = 0.0;
|
|
}
|
|
else
|
|
{
|
|
changePerStar = perStarExcessRatio;
|
|
horizontalChangeForIteration = changePerStar * totalStarFactors;
|
|
horizontalChange -= horizontalChangeForIteration;
|
|
}
|
|
|
|
for (int i = startIndex, count = Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
|
|
EGC.DataGridLength width = column.Width;
|
|
if (i == startIndex)
|
|
{
|
|
SetResizedColumnWidth(column, horizontalChangeForIteration, retainAuto);
|
|
}
|
|
else if (column.Width.IsStar && CanColumnParticipateInResize(column) && DoubleUtil.GreaterThan(width.DisplayValue, column.MinWidth))
|
|
{
|
|
double columnDesiredWidth = width.DisplayValue - (width.Value * changePerStar);
|
|
column.UpdateWidthForStarColumn(Math.Max(columnDesiredWidth, column.MinWidth), columnDesiredWidth, columnDesiredWidth / perStarWidth);
|
|
}
|
|
}
|
|
|
|
return horizontalChange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which recomputes widths of non star columns on positive resize of a column
|
|
/// </summary>
|
|
private double RecomputeNonStarColumnWidthsOnColumnPositiveResize(
|
|
double horizontalChange,
|
|
int resizingColumnIndex,
|
|
bool retainAuto)
|
|
{
|
|
if (DoubleUtil.GreaterThan(horizontalChange, 0.0))
|
|
{
|
|
double totalExcessWidth = 0.0;
|
|
bool iterationNeeded = true;
|
|
for (int i = Count - 1; iterationNeeded && i > resizingColumnIndex; i--)
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
|
|
if (!CanColumnParticipateInResize(column))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
double minWidth = column.MinWidth;
|
|
if (!width.IsStar &&
|
|
DoubleUtil.GreaterThan(width.DisplayValue, minWidth))
|
|
{
|
|
double columnExcessWidth = width.DisplayValue - minWidth;
|
|
if (DoubleUtil.GreaterThanOrClose(totalExcessWidth + columnExcessWidth, horizontalChange))
|
|
{
|
|
columnExcessWidth = horizontalChange - totalExcessWidth;
|
|
iterationNeeded = false;
|
|
}
|
|
|
|
column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue - columnExcessWidth));
|
|
totalExcessWidth += columnExcessWidth;
|
|
}
|
|
}
|
|
|
|
if (DoubleUtil.GreaterThan(totalExcessWidth, 0.0))
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(resizingColumnIndex);
|
|
SetResizedColumnWidth(column, totalExcessWidth, retainAuto);
|
|
horizontalChange -= totalExcessWidth;
|
|
}
|
|
}
|
|
|
|
return horizontalChange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which recomputes the widths of columns on negative resize of a column
|
|
/// </summary>
|
|
private void RecomputeColumnWidthsOnColumnNegativeResize(
|
|
double horizontalChange,
|
|
int resizingColumnIndex,
|
|
bool retainAuto)
|
|
{
|
|
double perStarWidth = 0.0;
|
|
if (HasVisibleStarColumnsInternal(out perStarWidth))
|
|
{
|
|
// increasing columns to the right which are less than the desired size
|
|
horizontalChange = RecomputeNonStarColumnWidthsOnColumnNegativeResize(horizontalChange, resizingColumnIndex, retainAuto);
|
|
|
|
// increasing star columns to the right
|
|
horizontalChange = RecomputeStarColumnWidthsOnColumnNegativeResize(horizontalChange, resizingColumnIndex, perStarWidth, retainAuto);
|
|
|
|
if (DoubleUtil.GreaterThan(horizontalChange, 0.0))
|
|
{
|
|
EGC.DataGridColumn resizingColumn = ColumnFromDisplayIndex(resizingColumnIndex);
|
|
if (!resizingColumn.Width.IsStar)
|
|
{
|
|
SetResizedColumnWidth(resizingColumn, -horizontalChange, retainAuto);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(resizingColumnIndex);
|
|
SetResizedColumnWidth(column, -horizontalChange, retainAuto);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which recomputes widths of non star columns on negative resize of a column
|
|
/// </summary>
|
|
private double RecomputeNonStarColumnWidthsOnColumnNegativeResize(
|
|
double horizontalChange,
|
|
int resizingColumnIndex,
|
|
bool retainAuto)
|
|
{
|
|
if (DoubleUtil.GreaterThan(horizontalChange, 0.0))
|
|
{
|
|
double totalLagWidth = 0.0;
|
|
bool iterationNeeded = true;
|
|
for (int i = resizingColumnIndex + 1, count = Count; iterationNeeded && i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
|
|
if (!CanColumnParticipateInResize(column))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
if (!width.IsStar &&
|
|
DoubleUtil.LessThan(width.DisplayValue, width.DesiredValue) &&
|
|
!DoubleUtil.AreClose(width.DisplayValue, column.MaxWidth))
|
|
{
|
|
double columnLagWidth = width.DesiredValue - width.DisplayValue;
|
|
if (DoubleUtil.GreaterThanOrClose(totalLagWidth + columnLagWidth, horizontalChange))
|
|
{
|
|
columnLagWidth = horizontalChange - totalLagWidth;
|
|
iterationNeeded = false;
|
|
}
|
|
|
|
column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue + columnLagWidth));
|
|
totalLagWidth += columnLagWidth;
|
|
}
|
|
}
|
|
|
|
if (DoubleUtil.GreaterThan(totalLagWidth, 0.0))
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(resizingColumnIndex);
|
|
SetResizedColumnWidth(column, -totalLagWidth, retainAuto);
|
|
horizontalChange -= totalLagWidth;
|
|
}
|
|
}
|
|
|
|
return horizontalChange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which recomputes widths on star columns on negative resize of a column
|
|
/// </summary>
|
|
private double RecomputeStarColumnWidthsOnColumnNegativeResize(
|
|
double horizontalChange,
|
|
int resizingColumnIndex,
|
|
double perStarWidth,
|
|
bool retainAuto)
|
|
{
|
|
while (DoubleUtil.GreaterThan(horizontalChange, 0.0))
|
|
{
|
|
double minPerStarLagRatio = Double.PositiveInfinity;
|
|
double rightStarFactors = GetStarFactorsForNegativeResize(resizingColumnIndex + 1, out minPerStarLagRatio);
|
|
|
|
if (DoubleUtil.GreaterThan(rightStarFactors, 0.0))
|
|
{
|
|
horizontalChange = ReallocateStarValuesForNegativeResize(
|
|
resizingColumnIndex,
|
|
horizontalChange,
|
|
minPerStarLagRatio,
|
|
rightStarFactors,
|
|
perStarWidth,
|
|
retainAuto);
|
|
|
|
if (DoubleUtil.AreClose(horizontalChange, 0.0))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return horizontalChange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which returns the total star factors of columns which resize of negative resize of a column
|
|
/// </summary>
|
|
private double GetStarFactorsForNegativeResize(int startIndex, out double minPerStarLagRatio)
|
|
{
|
|
minPerStarLagRatio = Double.PositiveInfinity;
|
|
double rightStarFactors = 0.0;
|
|
for (int i = startIndex, count = Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
|
|
if (!CanColumnParticipateInResize(column))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
if (width.IsStar && !DoubleUtil.AreClose(width.Value, 0.0))
|
|
{
|
|
if (DoubleUtil.LessThan(width.DisplayValue, column.MaxWidth))
|
|
{
|
|
rightStarFactors += width.Value;
|
|
double lagRatio = (column.MaxWidth - width.DisplayValue) / width.Value;
|
|
if (DoubleUtil.LessThan(lagRatio, minPerStarLagRatio))
|
|
{
|
|
minPerStarLagRatio = lagRatio;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rightStarFactors;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which reallocates star factors of columns on negative resize of a column
|
|
/// </summary>
|
|
private double ReallocateStarValuesForNegativeResize(
|
|
int startIndex,
|
|
double horizontalChange,
|
|
double perStarLagRatio,
|
|
double totalStarFactors,
|
|
double perStarWidth,
|
|
bool retainAuto)
|
|
{
|
|
double changePerStar = 0.0;
|
|
double horizontalChangeForIteration = 0.0;
|
|
if (DoubleUtil.LessThan(horizontalChange, perStarLagRatio * totalStarFactors))
|
|
{
|
|
changePerStar = horizontalChange / totalStarFactors;
|
|
horizontalChangeForIteration = horizontalChange;
|
|
horizontalChange = 0.0;
|
|
}
|
|
else
|
|
{
|
|
changePerStar = perStarLagRatio;
|
|
horizontalChangeForIteration = changePerStar * totalStarFactors;
|
|
horizontalChange -= horizontalChangeForIteration;
|
|
}
|
|
|
|
for (int i = startIndex, count = Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
|
|
EGC.DataGridLength width = column.Width;
|
|
if (i == startIndex)
|
|
{
|
|
SetResizedColumnWidth(column, -horizontalChangeForIteration, retainAuto);
|
|
}
|
|
else if (column.Width.IsStar && CanColumnParticipateInResize(column) && DoubleUtil.LessThan(width.DisplayValue, column.MaxWidth))
|
|
{
|
|
double columnDesiredWidth = width.DisplayValue + (width.Value * changePerStar);
|
|
column.UpdateWidthForStarColumn(Math.Min(columnDesiredWidth, column.MaxWidth), columnDesiredWidth, columnDesiredWidth / perStarWidth);
|
|
}
|
|
}
|
|
|
|
return horizontalChange;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which sets the width of the column which is currently getting resized
|
|
/// </summary>
|
|
private static void SetResizedColumnWidth(EGC.DataGridColumn column, double widthDelta, bool retainAuto)
|
|
{
|
|
EGC.DataGridLength width = column.Width;
|
|
double columnDisplayWidth = EGC.DataGridHelper.CoerceToMinMax(width.DisplayValue + widthDelta, column.MinWidth, column.MaxWidth);
|
|
|
|
if (width.IsStar)
|
|
{
|
|
double starValue = width.DesiredValue / width.Value;
|
|
column.UpdateWidthForStarColumn(columnDisplayWidth, columnDisplayWidth, columnDisplayWidth / starValue);
|
|
}
|
|
else if (!width.IsAbsolute && retainAuto)
|
|
{
|
|
column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, columnDisplayWidth));
|
|
}
|
|
else
|
|
{
|
|
column.SetWidthInternal(new EGC.DataGridLength(columnDisplayWidth, EGC.DataGridLengthUnitType.Pixel, columnDisplayWidth, columnDisplayWidth));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Width Give Away Methods
|
|
|
|
/// <summary>
|
|
/// Method which tries to give away the given amount of width
|
|
/// among all the columns except the ignored column
|
|
/// </summary>
|
|
private double GiveAwayWidthToColumns(EGC.DataGridColumn ignoredColumn, double giveAwayWidth)
|
|
{
|
|
return GiveAwayWidthToColumns(ignoredColumn, giveAwayWidth, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which tries to give away the given amount of width
|
|
/// among all the columns except the ignored column
|
|
/// </summary>
|
|
private double GiveAwayWidthToColumns(EGC.DataGridColumn ignoredColumn, double giveAwayWidth, bool recomputeStars)
|
|
{
|
|
double originalGiveAwayWidth = giveAwayWidth;
|
|
giveAwayWidth = GiveAwayWidthToScrollViewerExcess(giveAwayWidth);
|
|
giveAwayWidth = GiveAwayWidthToNonStarColumns(ignoredColumn, giveAwayWidth);
|
|
|
|
if (DoubleUtil.GreaterThan(giveAwayWidth, 0.0) || recomputeStars)
|
|
{
|
|
double sumOfStarDisplayWidths = 0.0;
|
|
double sumOfStarMaxWidths = 0.0;
|
|
bool giveAwayWidthIncluded = false;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
EGC.DataGridLength width = column.Width;
|
|
if (width.IsStar && column.IsVisible)
|
|
{
|
|
if (column == ignoredColumn)
|
|
{
|
|
giveAwayWidthIncluded = true;
|
|
}
|
|
|
|
sumOfStarDisplayWidths += width.DisplayValue;
|
|
sumOfStarMaxWidths += column.MaxWidth;
|
|
}
|
|
}
|
|
|
|
double expectedStarSpace = sumOfStarDisplayWidths;
|
|
if (!giveAwayWidthIncluded)
|
|
{
|
|
expectedStarSpace += giveAwayWidth;
|
|
}
|
|
else if (!DoubleUtil.AreClose(originalGiveAwayWidth, giveAwayWidth))
|
|
{
|
|
expectedStarSpace -= (originalGiveAwayWidth - giveAwayWidth);
|
|
}
|
|
|
|
double usedStarSpace = ComputeStarColumnWidths(Math.Min(expectedStarSpace, sumOfStarMaxWidths));
|
|
giveAwayWidth = Math.Max(usedStarSpace - expectedStarSpace, 0.0);
|
|
}
|
|
|
|
return giveAwayWidth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which tries to give away the given amount of width
|
|
/// among all non star columns except the ignored column
|
|
/// </summary>
|
|
private double GiveAwayWidthToNonStarColumns(EGC.DataGridColumn ignoredColumn, double giveAwayWidth)
|
|
{
|
|
while (DoubleUtil.GreaterThan(giveAwayWidth, 0.0))
|
|
{
|
|
int countOfParticipatingColumns = 0;
|
|
double minLagWidth = FindMinimumLaggingWidthOfNonStarColumns(
|
|
ignoredColumn,
|
|
out countOfParticipatingColumns);
|
|
|
|
if (countOfParticipatingColumns == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
double minTotalLagWidth = minLagWidth * countOfParticipatingColumns;
|
|
if (DoubleUtil.GreaterThanOrClose(minTotalLagWidth, giveAwayWidth))
|
|
{
|
|
minLagWidth = giveAwayWidth / countOfParticipatingColumns;
|
|
giveAwayWidth = 0.0;
|
|
}
|
|
else
|
|
{
|
|
giveAwayWidth -= minTotalLagWidth;
|
|
}
|
|
|
|
GiveAwayWidthToEveryNonStarColumn(ignoredColumn, minLagWidth);
|
|
}
|
|
|
|
return giveAwayWidth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which finds the minimum non-zero difference between displayvalue and desiredvalue
|
|
/// among all non star columns
|
|
/// </summary>
|
|
private double FindMinimumLaggingWidthOfNonStarColumns(
|
|
EGC.DataGridColumn ignoredColumn,
|
|
out int countOfParticipatingColumns)
|
|
{
|
|
double minLagWidth = Double.PositiveInfinity;
|
|
countOfParticipatingColumns = 0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (ignoredColumn == column ||
|
|
!column.IsVisible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
if (width.IsStar)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
double columnMaxWidth = column.MaxWidth;
|
|
if (DoubleUtil.LessThan(width.DisplayValue, width.DesiredValue) &&
|
|
!DoubleUtil.AreClose(width.DisplayValue, columnMaxWidth))
|
|
{
|
|
countOfParticipatingColumns++;
|
|
double lagWidth = Math.Min(width.DesiredValue, columnMaxWidth) - width.DisplayValue;
|
|
if (DoubleUtil.LessThan(lagWidth, minLagWidth))
|
|
{
|
|
minLagWidth = lagWidth;
|
|
}
|
|
}
|
|
}
|
|
|
|
return minLagWidth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which gives away the given amount of width to
|
|
/// every non star column whose display value is less than its desired value
|
|
/// </summary>
|
|
private void GiveAwayWidthToEveryNonStarColumn(EGC.DataGridColumn ignoredColumn, double perColumnGiveAwayWidth)
|
|
{
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (ignoredColumn == column ||
|
|
!column.IsVisible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
if (width.IsStar)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (DoubleUtil.LessThan(width.DisplayValue, Math.Min(width.DesiredValue, column.MaxWidth)))
|
|
{
|
|
column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue + perColumnGiveAwayWidth));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which gives away width to scroll viewer
|
|
/// if its extent width is greater than viewport width
|
|
/// </summary>
|
|
private double GiveAwayWidthToScrollViewerExcess(double giveAwayWidth)
|
|
{
|
|
double totalSpace = DataGridOwner.GetViewportWidthForColumns();
|
|
double usedSpace = 0.0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (column.IsVisible)
|
|
{
|
|
usedSpace += column.Width.DisplayValue;
|
|
}
|
|
}
|
|
|
|
if (DoubleUtil.GreaterThan(usedSpace, totalSpace))
|
|
{
|
|
double contributingSpace = usedSpace - totalSpace;
|
|
giveAwayWidth -= Math.Min(contributingSpace, giveAwayWidth);
|
|
}
|
|
|
|
return giveAwayWidth;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Width Take Away Methods
|
|
|
|
/// <summary>
|
|
/// Method which tries to get the unused column space when another column tries to positive resize
|
|
/// </summary>
|
|
private double TakeAwayUnusedSpaceOnColumnPositiveResize(double horizontalChange, int resizingColumnIndex, bool retainAuto)
|
|
{
|
|
double spaceNeeded = TakeAwayWidthFromUnusedSpace(false, horizontalChange);
|
|
if (DoubleUtil.LessThan(spaceNeeded, horizontalChange))
|
|
{
|
|
EGC.DataGridColumn resizingColumn = ColumnFromDisplayIndex(resizingColumnIndex);
|
|
SetResizedColumnWidth(resizingColumn, horizontalChange - spaceNeeded, retainAuto);
|
|
}
|
|
|
|
return spaceNeeded;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which tries to take away width from unused space
|
|
/// </summary>
|
|
private double TakeAwayWidthFromUnusedSpace(bool spaceAlreadyUtilized, double takeAwayWidth, double totalAvailableWidth)
|
|
{
|
|
double usedSpace = 0.0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (column.IsVisible)
|
|
{
|
|
usedSpace += column.Width.DisplayValue;
|
|
}
|
|
}
|
|
|
|
if (spaceAlreadyUtilized)
|
|
{
|
|
if (DoubleUtil.GreaterThanOrClose(totalAvailableWidth, usedSpace))
|
|
{
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
return Math.Min(usedSpace - totalAvailableWidth, takeAwayWidth);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double unusedSpace = totalAvailableWidth - usedSpace;
|
|
if (DoubleUtil.GreaterThan(unusedSpace, 0.0))
|
|
{
|
|
takeAwayWidth = Math.Max(0.0, takeAwayWidth - unusedSpace);
|
|
}
|
|
|
|
return takeAwayWidth;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which tries to take away width from unused space
|
|
/// </summary>
|
|
private double TakeAwayWidthFromUnusedSpace(bool spaceAlreadyUtilized, double takeAwayWidth)
|
|
{
|
|
double totalAvailableWidth = DataGridOwner.GetViewportWidthForColumns();
|
|
if (DoubleUtil.GreaterThan(totalAvailableWidth, 0.0))
|
|
{
|
|
return TakeAwayWidthFromUnusedSpace(spaceAlreadyUtilized, takeAwayWidth, totalAvailableWidth);
|
|
}
|
|
|
|
return takeAwayWidth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which tries to take away the given amount of width from columns
|
|
/// except the ignored column
|
|
/// </summary>
|
|
private double TakeAwayWidthFromColumns(EGC.DataGridColumn ignoredColumn, double takeAwayWidth, bool widthAlreadyUtilized)
|
|
{
|
|
double totalAvailableWidth = DataGridOwner.GetViewportWidthForColumns();
|
|
return TakeAwayWidthFromColumns(ignoredColumn, takeAwayWidth, widthAlreadyUtilized, totalAvailableWidth);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which tries to take away the given amount of width from columns
|
|
/// except the ignored column
|
|
/// </summary>
|
|
private double TakeAwayWidthFromColumns(EGC.DataGridColumn ignoredColumn, double takeAwayWidth, bool widthAlreadyUtilized, double totalAvailableWidth)
|
|
{
|
|
takeAwayWidth = TakeAwayWidthFromUnusedSpace(widthAlreadyUtilized, takeAwayWidth, totalAvailableWidth);
|
|
|
|
takeAwayWidth = TakeAwayWidthFromStarColumns(ignoredColumn, takeAwayWidth);
|
|
|
|
takeAwayWidth = TakeAwayWidthFromNonStarColumns(ignoredColumn, takeAwayWidth);
|
|
return takeAwayWidth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which tries to take away the given amount of width form
|
|
/// the star columns
|
|
/// </summary>
|
|
private double TakeAwayWidthFromStarColumns(EGC.DataGridColumn ignoredColumn, double takeAwayWidth)
|
|
{
|
|
if (DoubleUtil.GreaterThan(takeAwayWidth, 0.0))
|
|
{
|
|
double sumOfStarDisplayWidths = 0.0;
|
|
double sumOfStarMinWidths = 0.0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
EGC.DataGridLength width = column.Width;
|
|
if (width.IsStar && column.IsVisible)
|
|
{
|
|
if (column == ignoredColumn)
|
|
{
|
|
sumOfStarDisplayWidths += takeAwayWidth;
|
|
}
|
|
|
|
sumOfStarDisplayWidths += width.DisplayValue;
|
|
sumOfStarMinWidths += column.MinWidth;
|
|
}
|
|
}
|
|
|
|
double expectedStarSpace = sumOfStarDisplayWidths - takeAwayWidth;
|
|
double usedStarSpace = ComputeStarColumnWidths(Math.Max(expectedStarSpace, sumOfStarMinWidths));
|
|
takeAwayWidth = Math.Max(usedStarSpace - expectedStarSpace, 0.0);
|
|
}
|
|
|
|
return takeAwayWidth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which tries to take away the given amount of width
|
|
/// among all non star columns except the ignored column
|
|
/// </summary>
|
|
private double TakeAwayWidthFromNonStarColumns(EGC.DataGridColumn ignoredColumn, double takeAwayWidth)
|
|
{
|
|
while (DoubleUtil.GreaterThan(takeAwayWidth, 0.0))
|
|
{
|
|
int countOfParticipatingColumns = 0;
|
|
double minExcessWidth = FindMinimumExcessWidthOfNonStarColumns(
|
|
ignoredColumn,
|
|
out countOfParticipatingColumns);
|
|
|
|
if (countOfParticipatingColumns == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
double minTotalExcessWidth = minExcessWidth * countOfParticipatingColumns;
|
|
if (DoubleUtil.GreaterThanOrClose(minTotalExcessWidth, takeAwayWidth))
|
|
{
|
|
minExcessWidth = takeAwayWidth / countOfParticipatingColumns;
|
|
takeAwayWidth = 0.0;
|
|
}
|
|
else
|
|
{
|
|
takeAwayWidth -= minTotalExcessWidth;
|
|
}
|
|
|
|
TakeAwayWidthFromEveryNonStarColumn(ignoredColumn, minExcessWidth);
|
|
}
|
|
|
|
return takeAwayWidth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which finds the minimum non-zero difference between displayvalue and minwidth
|
|
/// among all non star columns
|
|
/// </summary>
|
|
private double FindMinimumExcessWidthOfNonStarColumns(
|
|
EGC.DataGridColumn ignoredColumn,
|
|
out int countOfParticipatingColumns)
|
|
{
|
|
double minExcessWidth = Double.PositiveInfinity;
|
|
countOfParticipatingColumns = 0;
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (ignoredColumn == column ||
|
|
!column.IsVisible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
if (width.IsStar)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
double minWidth = column.MinWidth;
|
|
if (DoubleUtil.GreaterThan(width.DisplayValue, minWidth))
|
|
{
|
|
countOfParticipatingColumns++;
|
|
double excessWidth = width.DisplayValue - minWidth;
|
|
if (DoubleUtil.LessThan(excessWidth, minExcessWidth))
|
|
{
|
|
minExcessWidth = excessWidth;
|
|
}
|
|
}
|
|
}
|
|
|
|
return minExcessWidth;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method which takes away the given amount of width from
|
|
/// every non star column whose display value is greater than its minwidth
|
|
/// </summary>
|
|
private void TakeAwayWidthFromEveryNonStarColumn(
|
|
EGC.DataGridColumn ignoredColumn,
|
|
double perColumnTakeAwayWidth)
|
|
{
|
|
foreach (EGC.DataGridColumn column in this)
|
|
{
|
|
if (ignoredColumn == column ||
|
|
!column.IsVisible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EGC.DataGridLength width = column.Width;
|
|
if (width.IsStar)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (DoubleUtil.GreaterThan(width.DisplayValue, column.MinWidth))
|
|
{
|
|
column.SetWidthInternal(new EGC.DataGridLength(width.Value, width.UnitType, width.DesiredValue, width.DisplayValue - perColumnTakeAwayWidth));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Column Virtualization
|
|
|
|
/// <summary>
|
|
/// Property which indicates that the RealizedColumnsBlockList
|
|
/// is dirty and needs to be rebuilt for non-column virtualized rows
|
|
/// </summary>
|
|
internal bool RebuildRealizedColumnsBlockListForNonVirtualizedRows
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// List of realized column index blocks for non-column virtualized rows
|
|
/// </summary>
|
|
internal List<RealizedColumnsBlock> RealizedColumnsBlockListForNonVirtualizedRows
|
|
{
|
|
get
|
|
{
|
|
return _realizedColumnsBlockListForNonVirtualizedRows;
|
|
}
|
|
|
|
set
|
|
{
|
|
_realizedColumnsBlockListForNonVirtualizedRows = value;
|
|
|
|
// Notify other rows and column header row to
|
|
// remeasure their child panel's in order to be
|
|
// in sync with latest column realization computations
|
|
EGC.DataGrid dataGrid = DataGridOwner;
|
|
dataGrid.NotifyPropertyChanged(
|
|
dataGrid,
|
|
"RealizedColumnsBlockListForNonVirtualizedRows",
|
|
new DependencyPropertyChangedEventArgs(),
|
|
NotificationTarget.CellsPresenter | NotificationTarget.ColumnHeadersPresenter);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// List of realized column display index blocks for non-column virtualized rows
|
|
/// </summary>
|
|
internal List<RealizedColumnsBlock> RealizedColumnsDisplayIndexBlockListForNonVirtualizedRows
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property which indicates that the RealizedColumnsBlockList
|
|
/// is dirty and needs to be rebuilt for column virtualized rows
|
|
/// </summary>
|
|
internal bool RebuildRealizedColumnsBlockListForVirtualizedRows
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// List of realized column index blocks for column virtualized rows
|
|
/// </summary>
|
|
internal List<RealizedColumnsBlock> RealizedColumnsBlockListForVirtualizedRows
|
|
{
|
|
get
|
|
{
|
|
return _realizedColumnsBlockListForVirtualizedRows;
|
|
}
|
|
|
|
set
|
|
{
|
|
_realizedColumnsBlockListForVirtualizedRows = value;
|
|
|
|
// Notify other rows and column header row to
|
|
// remeasure their child panel's in order to be
|
|
// in sync with latest column realization computations
|
|
EGC.DataGrid dataGrid = DataGridOwner;
|
|
dataGrid.NotifyPropertyChanged(
|
|
dataGrid,
|
|
"RealizedColumnsBlockListForVirtualizedRows",
|
|
new DependencyPropertyChangedEventArgs(),
|
|
NotificationTarget.CellsPresenter | NotificationTarget.ColumnHeadersPresenter);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// List of realized column display index blocks for column virtualized rows
|
|
/// </summary>
|
|
internal List<RealizedColumnsBlock> RealizedColumnsDisplayIndexBlockListForVirtualizedRows
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when properties which affect the realized columns namely
|
|
/// Column Width, FrozenColumnCount, DisplayIndex etc. are changed.
|
|
/// </summary>
|
|
internal void InvalidateColumnRealization(bool invalidateForNonVirtualizedRows)
|
|
{
|
|
RebuildRealizedColumnsBlockListForVirtualizedRows = true;
|
|
if (invalidateForNonVirtualizedRows)
|
|
{
|
|
RebuildRealizedColumnsBlockListForNonVirtualizedRows = true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Hidden Columns
|
|
|
|
/// <summary>
|
|
/// Helper property to return the display index of first visible column
|
|
/// </summary>
|
|
internal int FirstVisibleDisplayIndex
|
|
{
|
|
get
|
|
{
|
|
for (int i = 0, count = this.Count; i < count; i++)
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
|
|
if (column.IsVisible)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper property to return the display index of last visible column
|
|
/// </summary>
|
|
internal int LastVisibleDisplayIndex
|
|
{
|
|
get
|
|
{
|
|
for (int i = this.Count - 1; i >= 0; i--)
|
|
{
|
|
EGC.DataGridColumn column = ColumnFromDisplayIndex(i);
|
|
if (column.IsVisible)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Data
|
|
|
|
private EGC.DataGrid _dataGridOwner;
|
|
private bool _isUpdatingDisplayIndex; // true if we're in the middle of updating the display index of each column.
|
|
private List<int> _displayIndexMap; // maps a DisplayIndex to an index in the _columns collection.
|
|
private bool _displayIndexMapInitialized; // Flag is used to delay the validation of DisplayIndex until the first measure
|
|
private bool _isClearingDisplayIndex; // Flag indicating that we're currently clearing the display index. We should not coerce default display index's during this time.
|
|
private bool _columnWidthsComputationPending; // Flag indicating whether the columns width computaion operation is pending
|
|
private Dictionary<EGC.DataGridColumn, EGC.DataGridLength> _originalWidthsForResize; // Dictionary to hold the original widths of columns for resize operation
|
|
private double? _averageColumnWidth = null; // average width of all visible columns
|
|
private List<RealizedColumnsBlock> _realizedColumnsBlockListForNonVirtualizedRows = null; // Realized columns for non-virtualized rows
|
|
private List<RealizedColumnsBlock> _realizedColumnsBlockListForVirtualizedRows = null; // Realized columns for virtualized rows
|
|
|
|
#endregion
|
|
}
|
|
} |