Browse Source

Implement support of NthChild in DataGrid

pull/7292/head
Max Katz 5 years ago
parent
commit
d083acc20d
  1. 19
      samples/ControlCatalog/Pages/DataGridPage.xaml
  2. 10
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  3. 2
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  4. 36
      src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs
  5. 9
      src/Avalonia.Controls.DataGrid/DataGridColumns.cs
  6. 36
      src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
  7. 39
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  8. 29
      src/Avalonia.Controls.DataGrid/DataGridRows.cs
  9. 39
      src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs
  10. 36
      src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs
  11. 20
      src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs
  12. 12
      src/Avalonia.Controls.DataGrid/Themes/Default.xaml
  13. 8
      src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
  14. 2
      src/Avalonia.Controls/ItemsControl.cs
  15. 2
      src/Avalonia.Controls/Panel.cs
  16. 2
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  17. 2
      src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs

19
samples/ControlCatalog/Pages/DataGridPage.xaml

@ -13,9 +13,22 @@
</UserControl.Resources> </UserControl.Resources>
<UserControl.Styles> <UserControl.Styles>
<Style Selector="DataGridCell.gdp"> <Style Selector="DataGridCell.gdp">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" /> <Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" />
</Style> </Style>
<Style Selector="DataGridColumnHeader:nth-last-child(1)">
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="DataGridCell:nth-last-child(1)">
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="DataGrid#dataGridGrouping DataGridRow:nth-child(5n+3)">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="DataGrid#dataGridGrouping DataGridRow:nth-last-child(5n+1)">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</UserControl.Styles> </UserControl.Styles>
<Grid RowDefinitions="Auto,Auto,*"> <Grid RowDefinitions="Auto,Auto,*">
<StackPanel Orientation="Vertical" Spacing="4" Grid.Row="0"> <StackPanel Orientation="Vertical" Spacing="4" Grid.Row="0">
@ -31,7 +44,9 @@
<DockPanel> <DockPanel>
<CheckBox x:Name="ShowGDP" IsChecked="True" Content="Toggle GDP Column Visibility" <CheckBox x:Name="ShowGDP" IsChecked="True" Content="Toggle GDP Column Visibility"
DockPanel.Dock="Top"/> DockPanel.Dock="Top"/>
<DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All"> <DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All"
RowBackground="#1000"
AlternatingRowBackground="#1fff">
<DataGrid.Columns> <DataGrid.Columns>
<!-- Using HeaderTemplate --> <!-- Using HeaderTemplate -->
<DataGridTextColumn Header="Country" HeaderTemplate="{StaticResource Demo.DataTemplates.CountryHeader}" Binding="{Binding Name}" Width="6*" /> <DataGridTextColumn Header="Country" HeaderTemplate="{StaticResource Demo.DataTemplates.CountryHeader}" Binding="{Binding Name}" Width="6*" />

10
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -669,8 +669,6 @@ namespace Avalonia.Controls
ItemsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnItemsPropertyChanged(e)); ItemsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnItemsPropertyChanged(e));
CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnCanUserResizeColumnsChanged(e)); CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnCanUserResizeColumnsChanged(e));
ColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnColumnWidthChanged(e)); ColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnColumnWidthChanged(e));
RowBackgroundProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowBackgroundChanged(e));
AlternatingRowBackgroundProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowBackgroundChanged(e));
FrozenColumnCountProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnFrozenColumnCountChanged(e)); FrozenColumnCountProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnFrozenColumnCountChanged(e));
GridLinesVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnGridLinesVisibilityChanged(e)); GridLinesVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnGridLinesVisibilityChanged(e));
HeadersVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnHeadersVisibilityChanged(e)); HeadersVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnHeadersVisibilityChanged(e));
@ -1144,14 +1142,6 @@ namespace Avalonia.Controls
InvalidateCellsArrange(); InvalidateCellsArrange();
} }
private void OnRowBackgroundChanged(AvaloniaPropertyChangedEventArgs e)
{
foreach (DataGridRow row in GetAllRows())
{
row.EnsureBackground();
}
}
private void OnColumnWidthChanged(AvaloniaPropertyChangedEventArgs e) private void OnColumnWidthChanged(AvaloniaPropertyChangedEventArgs e)
{ {
var value = (DataGridLength)e.NewValue; var value = (DataGridLength)e.NewValue;

2
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -199,7 +199,7 @@ namespace Avalonia.Controls
if (change.Property == IsVisibleProperty) if (change.Property == IsVisibleProperty)
{ {
OwningGrid?.OnColumnVisibleStateChanging(this); OwningGrid?.OnColumnVisibleStateChanging(this);
var isVisible = (change as AvaloniaPropertyChangedEventArgs<bool>).NewValue.Value; var isVisible = change.NewValue.GetValueOrDefault<bool>();
if (_headerCell != null) if (_headerCell != null)
{ {

36
src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs

@ -12,7 +12,8 @@ namespace Avalonia.Controls
{ {
internal class DataGridColumnCollection : ObservableCollection<DataGridColumn> internal class DataGridColumnCollection : ObservableCollection<DataGridColumn>
{ {
private DataGrid _owningGrid; private readonly Dictionary<int, int> _columnsMap = new Dictionary<int, int>();
private readonly DataGrid _owningGrid;
public DataGridColumnCollection(DataGrid owningGrid) public DataGridColumnCollection(DataGrid owningGrid)
{ {
@ -124,18 +125,8 @@ namespace Avalonia.Controls
internal int VisibleColumnCount internal int VisibleColumnCount
{ {
get get;
{ private set;
int visibleColumnCount = 0;
for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++)
{
if (ItemsInternal[columnIndex].IsVisible)
{
visibleColumnCount++;
}
}
return visibleColumnCount;
}
} }
internal double VisibleEdgedColumnsWidth internal double VisibleEdgedColumnsWidth
@ -287,20 +278,31 @@ namespace Avalonia.Controls
{ {
VisibleStarColumnCount = 0; VisibleStarColumnCount = 0;
VisibleEdgedColumnsWidth = 0; VisibleEdgedColumnsWidth = 0;
VisibleColumnCount = 0;
_columnsMap.Clear();
for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++) for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++)
{ {
if (ItemsInternal[columnIndex].IsVisible) var item = ItemsInternal[columnIndex];
_columnsMap[columnIndex] = item.DisplayIndex;
if (item.IsVisible)
{ {
ItemsInternal[columnIndex].EnsureWidth(); VisibleColumnCount++;
if (ItemsInternal[columnIndex].Width.IsStar) item.EnsureWidth();
if (item.Width.IsStar)
{ {
VisibleStarColumnCount++; VisibleStarColumnCount++;
} }
VisibleEdgedColumnsWidth += ItemsInternal[columnIndex].ActualWidth; VisibleEdgedColumnsWidth += item.ActualWidth;
} }
} }
} }
internal int GetColumnDisplayIndex(int columnIndex)
{
return _columnsMap.TryGetValue(columnIndex, out var displayIndex) ? displayIndex : -1;
}
internal DataGridColumn GetColumnAtDisplayIndex(int displayIndex) internal DataGridColumn GetColumnAtDisplayIndex(int displayIndex)
{ {
if (displayIndex < 0 || displayIndex >= ItemsInternal.Count || displayIndex >= DisplayIndexMap.Count) if (displayIndex < 0 || displayIndex >= ItemsInternal.Count || displayIndex >= DisplayIndexMap.Count)

9
src/Avalonia.Controls.DataGrid/DataGridColumns.cs

@ -444,12 +444,11 @@ namespace Avalonia.Controls
// We need to explicitly collapse the cells of the invisible column because layout only goes through // We need to explicitly collapse the cells of the invisible column because layout only goes through
// visible ones // visible ones
if (!updatedColumn.IsVisible) ColumnHeaders.InvalidateChildIndex();
foreach (var row in GetAllRows())
{ {
foreach (DataGridRow row in GetAllRows()) row.Cells[updatedColumn.Index].IsVisible = updatedColumn.IsVisible;
{ row.InvalidateCellsIndex();
row.Cells[updatedColumn.Index].IsVisible = false;
}
} }
} }

36
src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs

@ -77,7 +77,7 @@ namespace Avalonia.Controls
private set; private set;
} }
public int Count => GetCount(true); public int Count => TryGetCount(true, false, out var count) ? count : 0;
public bool DataIsPrimitive public bool DataIsPrimitive
{ {
@ -193,22 +193,25 @@ namespace Avalonia.Controls
} }
} }
internal bool Any()
{
return GetCount(false) > 0;
}
/// <param name="allowSlow">When "allowSlow" is false, method will not use Linq.Count() method and will return 0 or 1 instead.</param> /// <param name="allowSlow">When "allowSlow" is false, method will not use Linq.Count() method and will return 0 or 1 instead.</param>
private int GetCount(bool allowSlow) /// <param name="getAny">If "getAny" is true, method can use Linq.Any() method to speedup.</param>
internal bool TryGetCount(bool allowSlow, bool getAny, out int count)
{ {
return DataSource switch bool result;
(result, count) = DataSource switch
{ {
ICollection collection => collection.Count, ICollection collection => (true, collection.Count),
DataGridCollectionView cv => cv.Count, DataGridCollectionView cv => (true, cv.Count),
IEnumerable enumerable when allowSlow => enumerable.Cast<object>().Count(), IEnumerable enumerable when allowSlow && !getAny => (true, enumerable.Cast<object>().Count()),
IEnumerable enumerable when !allowSlow => enumerable.Cast<object>().Any() ? 1 : 0, IEnumerable enumerable when getAny => (true, enumerable.Cast<object>().Any() ? 1 : 0),
_ => 0 _ => (false, 0)
}; };
return result;
}
internal bool Any()
{
return TryGetCount(false, true, out var count) && count > 0;
} }
/// <summary> /// <summary>
@ -383,7 +386,7 @@ namespace Avalonia.Controls
List<string> propertyNames = TypeHelper.SplitPropertyPath(propertyName); List<string> propertyNames = TypeHelper.SplitPropertyPath(propertyName);
for (int i = 0; i < propertyNames.Count; i++) for (int i = 0; i < propertyNames.Count; i++)
{ {
propertyInfo = propertyType.GetPropertyOrIndexer(propertyNames[i], out object[] index); propertyInfo = propertyType.GetPropertyOrIndexer(propertyNames[i], out _);
if (propertyInfo == null || propertyType.GetIsReadOnly() || propertyInfo.GetIsReadOnly()) if (propertyInfo == null || propertyType.GetIsReadOnly() || propertyInfo.GetIsReadOnly())
{ {
// Either the data type is read-only, the property doesn't exist, or it does exist but is read-only // Either the data type is read-only, the property doesn't exist, or it does exist but is read-only
@ -391,11 +394,10 @@ namespace Avalonia.Controls
} }
// Check if EditableAttribute is defined on the property and if it indicates uneditable // Check if EditableAttribute is defined on the property and if it indicates uneditable
object[] attributes = propertyInfo.GetCustomAttributes(typeof(EditableAttribute), true); var attributes = propertyInfo.GetCustomAttributes(typeof(EditableAttribute), true);
if (attributes != null && attributes.Length > 0) if (attributes != null && attributes.Length > 0)
{ {
EditableAttribute editableAttribute = attributes[0] as EditableAttribute; var editableAttribute = (EditableAttribute)attributes[0];
Debug.Assert(editableAttribute != null);
if (!editableAttribute.AllowEdit) if (!editableAttribute.AllowEdit)
{ {
return true; return true;

39
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -543,7 +543,6 @@ namespace Avalonia.Controls
RootElement = e.NameScope.Find<Panel>(DATAGRIDROW_elementRoot); RootElement = e.NameScope.Find<Panel>(DATAGRIDROW_elementRoot);
if (RootElement != null) if (RootElement != null)
{ {
EnsureBackground();
UpdatePseudoClasses(); UpdatePseudoClasses();
} }
@ -668,43 +667,9 @@ namespace Avalonia.Controls
Slot = -1; Slot = -1;
} }
// Make sure the row's background is set to its correct value. It could be explicity set or inherit internal void InvalidateCellsIndex()
// DataGrid.RowBackground or DataGrid.AlternatingRowBackground
internal void EnsureBackground()
{ {
// Inherit the DataGrid's RowBackground properties only if this row doesn't explicity have a background set _cellsElement?.InvalidateChildIndex();
if (RootElement != null && OwningGrid != null)
{
IBrush newBackground = null;
if (Background == null)
{
if (Index % 2 == 0 || OwningGrid.AlternatingRowBackground == null)
{
// Use OwningGrid.RowBackground if the index is even or if the OwningGrid.AlternatingRowBackground is null
if (OwningGrid.RowBackground != null)
{
newBackground = OwningGrid.RowBackground;
}
}
else
{
// Alternate row
if (OwningGrid.AlternatingRowBackground != null)
{
newBackground = OwningGrid.AlternatingRowBackground;
}
}
}
else
{
newBackground = Background;
}
if (RootElement.Background != newBackground)
{
RootElement.Background = newBackground;
}
}
} }
internal void EnsureFillerVisibility() internal void EnsureFillerVisibility()

29
src/Avalonia.Controls.DataGrid/DataGridRows.cs

@ -5,6 +5,7 @@
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Utilities; using Avalonia.Utilities;
using System; using System;
@ -16,7 +17,7 @@ using System.Linq;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
public partial class DataGrid public partial class DataGrid : IChildIndexProvider
{ {
internal bool AreRowBottomGridLinesRequired internal bool AreRowBottomGridLinesRequired
@ -123,6 +124,26 @@ namespace Avalonia.Controls
} }
} }
internal EventHandler<ChildIndexChangedEventArgs> _childIndexChanged;
event EventHandler<ChildIndexChangedEventArgs> IChildIndexProvider.ChildIndexChanged
{
add => _childIndexChanged += value;
remove => _childIndexChanged -= value;
}
int IChildIndexProvider.GetChildIndex(ILogical child)
{
return child is DataGridRow row
? row.Index
: throw new InvalidOperationException("Invalid DataGrid child");
}
bool IChildIndexProvider.TryGetTotalCount(out int count)
{
return DataConnection.TryGetCount(false, true, out count);
}
/// <summary> /// <summary>
/// Clears the entire selection. Displayed rows are deselected explicitly to visualize /// Clears the entire selection. Displayed rows are deselected explicitly to visualize
/// potential transition effects /// potential transition effects
@ -811,7 +832,7 @@ namespace Avalonia.Controls
if (row.Slot > slotDeleted) if (row.Slot > slotDeleted)
{ {
CorrectRowAfterDeletion(row, wasRow); CorrectRowAfterDeletion(row, wasRow);
row.EnsureBackground(); _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row));
} }
} }
@ -867,7 +888,7 @@ namespace Avalonia.Controls
if (row.Slot >= slotInserted) if (row.Slot >= slotInserted)
{ {
DataGrid.CorrectRowAfterInsertion(row, rowInserted); DataGrid.CorrectRowAfterInsertion(row, rowInserted);
row.EnsureBackground(); _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row));
} }
} }
@ -1485,8 +1506,8 @@ namespace Avalonia.Controls
// If the row has been recycled, reapply the BackgroundBrush // If the row has been recycled, reapply the BackgroundBrush
if (row.IsRecycled) if (row.IsRecycled)
{ {
row.EnsureBackground();
row.ApplyCellsState(); row.ApplyCellsState();
_childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row));
} }
else if (row == EditingRow) else if (row == EditingRow)
{ {

39
src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs

@ -3,12 +3,13 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Utilities; using Avalonia.Utilities;
using System; using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using Avalonia.Controls;
using Avalonia.Controls.Utils;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
@ -16,9 +17,10 @@ namespace Avalonia.Controls.Primitives
/// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> /// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" />
/// to specify the location in the control's visual tree where the cells are to be added. /// to specify the location in the control's visual tree where the cells are to be added.
/// </summary> /// </summary>
public sealed class DataGridCellsPresenter : Panel public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider
{ {
private double _fillerLeftEdge; private double _fillerLeftEdge;
private EventHandler<ChildIndexChangedEventArgs> _childIndexChanged;
// The desired height needs to be cached due to column virtualization; otherwise, the cells // The desired height needs to be cached due to column virtualization; otherwise, the cells
// would grow and shrink as the DataGrid scrolls horizontally // would grow and shrink as the DataGrid scrolls horizontally
@ -42,6 +44,25 @@ namespace Avalonia.Controls.Primitives
set; set;
} }
event EventHandler<ChildIndexChangedEventArgs> IChildIndexProvider.ChildIndexChanged
{
add => _childIndexChanged += value;
remove => _childIndexChanged -= value;
}
int IChildIndexProvider.GetChildIndex(ILogical child)
{
return child is DataGridCell cell
? OwningGrid.ColumnsInternal.GetColumnDisplayIndex(cell.ColumnIndex)
: throw new InvalidOperationException("Invalid cell type");
}
bool IChildIndexProvider.TryGetTotalCount(out int count)
{
count = OwningGrid.ColumnsInternal.VisibleColumnCount;
return true;
}
/// <summary> /// <summary>
/// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" />. /// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" />.
/// </summary> /// </summary>
@ -120,6 +141,13 @@ namespace Avalonia.Controls.Primitives
} }
} }
protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
{
base.ChildrenChanged(sender, e);
InvalidateChildIndex();
}
private static void EnsureCellDisplay(DataGridCell cell, bool displayColumn) private static void EnsureCellDisplay(DataGridCell cell, bool displayColumn)
{ {
if (cell.IsCurrent) if (cell.IsCurrent)
@ -304,6 +332,11 @@ namespace Avalonia.Controls.Primitives
DesiredHeight = 0; DesiredHeight = 0;
} }
internal void InvalidateChildIndex()
{
_childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty);
}
private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge) private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge)
{ {
if (!column.IsVisible) if (!column.IsVisible)

36
src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs

@ -3,8 +3,10 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using System; using System;
using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
@ -13,10 +15,11 @@ namespace Avalonia.Controls.Primitives
/// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the /// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the
/// location in the control's visual tree where the column headers are to be added. /// location in the control's visual tree where the column headers are to be added.
/// </summary> /// </summary>
public sealed class DataGridColumnHeadersPresenter : Panel public sealed class DataGridColumnHeadersPresenter : Panel, IChildIndexProvider
{ {
private Control _dragIndicator; private Control _dragIndicator;
private IControl _dropLocationIndicator; private IControl _dropLocationIndicator;
private EventHandler<ChildIndexChangedEventArgs> _childIndexChanged;
/// <summary> /// <summary>
/// Tracks which column is currently being dragged. /// Tracks which column is currently being dragged.
@ -104,6 +107,25 @@ namespace Avalonia.Controls.Primitives
set; set;
} }
event EventHandler<ChildIndexChangedEventArgs> IChildIndexProvider.ChildIndexChanged
{
add => _childIndexChanged += value;
remove => _childIndexChanged -= value;
}
int IChildIndexProvider.GetChildIndex(ILogical child)
{
return child is DataGridColumnHeader header
? OwningGrid.ColumnsInternal.GetColumnDisplayIndex(header.ColumnIndex)
: throw new InvalidOperationException("Invalid cell type");
}
bool IChildIndexProvider.TryGetTotalCount(out int count)
{
count = OwningGrid.ColumnsInternal.VisibleColumnCount;
return true;
}
/// <summary> /// <summary>
/// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" />. /// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" />.
/// </summary> /// </summary>
@ -391,5 +413,17 @@ namespace Avalonia.Controls.Primitives
OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth(); OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
return new Size(OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, height); return new Size(OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, height);
} }
protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
{
base.ChildrenChanged(sender, e);
InvalidateChildIndex();
}
internal void InvalidateChildIndex()
{
_childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty);
}
} }
} }

20
src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs

@ -7,8 +7,8 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.GestureRecognizers;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
@ -17,7 +17,7 @@ namespace Avalonia.Controls.Primitives
/// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the /// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the
/// location in the control's visual tree where the rows are to be added. /// location in the control's visual tree where the rows are to be added.
/// </summary> /// </summary>
public sealed class DataGridRowsPresenter : Panel public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider
{ {
public DataGridRowsPresenter() public DataGridRowsPresenter()
{ {
@ -44,6 +44,22 @@ namespace Avalonia.Controls.Primitives
} }
} }
event EventHandler<ChildIndexChangedEventArgs> IChildIndexProvider.ChildIndexChanged
{
add => OwningGrid._childIndexChanged += value;
remove => OwningGrid._childIndexChanged -= value;
}
bool IChildIndexProvider.TryGetTotalCount(out int count)
{
return ((IChildIndexProvider)OwningGrid).TryGetTotalCount(out count);
}
int IChildIndexProvider.GetChildIndex(ILogical child)
{
return ((IChildIndexProvider)OwningGrid).GetChildIndex(child);
}
/// <summary> /// <summary>
/// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" />. /// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" />.
/// </summary> /// </summary>

12
src/Avalonia.Controls.DataGrid/Themes/Default.xaml

@ -103,8 +103,9 @@
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<DataGridFrozenGrid Name="PART_Root" <DataGridFrozenGrid Name="PART_Root"
RowDefinitions="*,Auto,Auto" Background="{TemplateBinding Background}"
ColumnDefinitions="Auto,*"> RowDefinitions="*,Auto,Auto"
ColumnDefinitions="Auto,*">
<Rectangle Name="BackgroundRectangle" Grid.RowSpan="2" Grid.ColumnSpan="2"/> <Rectangle Name="BackgroundRectangle" Grid.RowSpan="2" Grid.ColumnSpan="2"/>
@ -118,6 +119,13 @@
</Setter> </Setter>
</Style> </Style>
<Style Selector="DataGridRow">
<Setter Property="Background" Value="{Binding $parent[DataGrid].RowBackground}" />
</Style>
<Style Selector="DataGridRow:nth-child(even)">
<Setter Property="Background" Value="{Binding $parent[DataGrid].AlternatingRowBackground}" />
</Style>
<Style Selector="DataGridRow /template/ Rectangle#BackgroundRectangle"> <Style Selector="DataGridRow /template/ Rectangle#BackgroundRectangle">
<Setter Property="IsVisible" Value="False"/> <Setter Property="IsVisible" Value="False"/>
<Setter Property="Fill" Value="{DynamicResource HighlightBrush}" /> <Setter Property="Fill" Value="{DynamicResource HighlightBrush}" />

8
src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml

@ -272,6 +272,7 @@
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<DataGridFrozenGrid Name="PART_Root" <DataGridFrozenGrid Name="PART_Root"
Background="{TemplateBinding Background}"
RowDefinitions="*,Auto,Auto" RowDefinitions="*,Auto,Auto"
ColumnDefinitions="Auto,*"> ColumnDefinitions="Auto,*">
@ -303,6 +304,13 @@
</Setter> </Setter>
</Style> </Style>
<Style Selector="DataGridRow">
<Setter Property="Background" Value="{Binding $parent[DataGrid].RowBackground}" />
</Style>
<Style Selector="DataGridRow:nth-child(even)">
<Setter Property="Background" Value="{Binding $parent[DataGrid].AlternatingRowBackground}" />
</Style>
<Style Selector="DataGridRow /template/ Rectangle#InvalidVisualElement"> <Style Selector="DataGridRow /template/ Rectangle#InvalidVisualElement">
<Setter Property="Opacity" Value="0" /> <Setter Property="Opacity" Value="0" />
</Style> </Style>

2
src/Avalonia.Controls/ItemsControl.cs

@ -166,7 +166,7 @@ namespace Avalonia.Controls
if (Presenter is IChildIndexProvider innerProvider) if (Presenter is IChildIndexProvider innerProvider)
{ {
innerProvider.ChildIndexChanged += PresenterChildIndexChanged; innerProvider.ChildIndexChanged += PresenterChildIndexChanged;
_childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty);
} }
} }

2
src/Avalonia.Controls/Panel.cs

@ -147,7 +147,7 @@ namespace Avalonia.Controls
throw new NotSupportedException(); throw new NotSupportedException();
} }
_childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty);
InvalidateMeasureOnChildrenChanged(); InvalidateMeasureOnChildrenChanged();
} }

2
src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs

@ -158,7 +158,7 @@ namespace Avalonia.Controls.Presenters
{ {
ItemsChanged(e); ItemsChanged(e);
_childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty);
} }
} }

2
src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs

@ -8,6 +8,8 @@ namespace Avalonia.LogicalTree
/// </summary> /// </summary>
public class ChildIndexChangedEventArgs : EventArgs public class ChildIndexChangedEventArgs : EventArgs
{ {
public static new ChildIndexChangedEventArgs Empty { get; } = new ChildIndexChangedEventArgs();
public ChildIndexChangedEventArgs() public ChildIndexChangedEventArgs()
{ {
} }

Loading…
Cancel
Save