mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-12-02 03:57:38 +08:00
refactor(module: table): reorganize the selection of rows (#3502)
* refactor(module: selection): reorganize the selection of rows * hold the selected rows * fix select all * fix select all * fix header checkbox Indeterminate status * fix children selection * select all within children * fix CheckStrictly * fix children cache * use hashcode as the key of cache * fix refrence loop * fix circular reference expand * clean up * fix invoke table selected rows after check * clean up * prevent selected rows was changed multiple times * update bunit
This commit is contained in:
parent
1409c277b5
commit
dbb1978563
@ -4,10 +4,16 @@ namespace AntDesign
|
||||
{
|
||||
public interface ISelectionColumn : IColumn
|
||||
{
|
||||
public string Type { get; set; }
|
||||
|
||||
public bool Disabled { get; }
|
||||
|
||||
public string Key { get; }
|
||||
|
||||
public bool Selected { get; }
|
||||
|
||||
public bool CheckStrictly { get; set; }
|
||||
|
||||
public IList<ISelectionColumn> RowSelections { get; }
|
||||
|
||||
public void StateHasChanged();
|
||||
|
@ -54,7 +54,7 @@ namespace AntDesign
|
||||
|
||||
internal bool HasRowTemplate { get; }
|
||||
|
||||
internal void SelectionChanged();
|
||||
//internal void SelectionChanged();
|
||||
|
||||
internal void OnExpandChange(RowData rowData);
|
||||
|
||||
|
@ -31,6 +31,8 @@ namespace AntDesign
|
||||
|
||||
private bool IsHeaderDisabled => RowSelections.Any() && RowSelections.All(x => x.Disabled);
|
||||
|
||||
public bool Selected => DataItem.Selected;
|
||||
|
||||
private bool? _selected;
|
||||
|
||||
private void OnCheckedChange(bool selected)
|
||||
@ -54,7 +56,7 @@ namespace AntDesign
|
||||
}
|
||||
else
|
||||
{
|
||||
DataItem.Selected = selected;
|
||||
RowData.SetSelected(selected, CheckStrictly);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,6 +78,7 @@ namespace AntDesign
|
||||
else if (IsBody)
|
||||
{
|
||||
Table?.Selection?.RowSelections.Add(this);
|
||||
DataItem.Disabled = Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@
|
||||
}
|
||||
</CascadingValue>
|
||||
</tr>
|
||||
@body(_showItems, 0)
|
||||
@body(_showItems)
|
||||
</tbody>
|
||||
@tfoot(this)
|
||||
</table>
|
||||
@ -83,7 +83,7 @@
|
||||
}
|
||||
</CascadingValue>
|
||||
</tr>
|
||||
@body(_showItems, 0)
|
||||
@body(_showItems)
|
||||
</tbody>
|
||||
@tfoot(this)
|
||||
</table>
|
||||
@ -96,7 +96,7 @@
|
||||
@colGroup((this, true))
|
||||
@header(this)
|
||||
<tbody class="ant-table-tbody">
|
||||
@body( _showItems, 0)
|
||||
@body( _showItems)
|
||||
</tbody>
|
||||
@tfoot(this)
|
||||
</table>
|
||||
@ -182,21 +182,24 @@ RenderFragment<(Table<TItem> table, bool header)> colGroup = ctx =>
|
||||
<col style="width: @((CssSizeLength)table.ScrollBarWidth); min-width: @((CssSizeLength)table.ScrollBarWidth);" />
|
||||
}
|
||||
</colgroup>
|
||||
</Template>;
|
||||
</Template>
|
||||
;
|
||||
|
||||
RenderFragment body(IEnumerable<TItem> showItems, int level)
|
||||
{
|
||||
if (!_hasInitialized)
|
||||
RenderFragment body(IEnumerable<TItem> showItems, RowData<TItem> parentRowData = null)
|
||||
{
|
||||
return builder => { };
|
||||
}
|
||||
var level = (parentRowData?.Level + 1) ?? 0;
|
||||
|
||||
if (!_hasInitialized)
|
||||
{
|
||||
return builder => { };
|
||||
}
|
||||
#if NET5_0_OR_GREATER
|
||||
if (UseItemsProvider&&!_isVirtualizeEmpty)
|
||||
{
|
||||
return @<Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize TItem="RowData<TItem>"
|
||||
OverscanCount="10"
|
||||
ItemsProvider="ItemsProvider"
|
||||
ItemContent="bodyRow()" >
|
||||
ItemContent="bodyRow()">
|
||||
<Placeholder>
|
||||
<tr class="ant-table-row ant-table-row-level-0">
|
||||
@foreach (var item in ColumnContext.HeaderColumns)
|
||||
@ -238,15 +241,16 @@ RenderFragment<(Table<TItem> table, bool header)> colGroup = ctx =>
|
||||
{
|
||||
return@<ForeachLoop
|
||||
TItem="RowData<TItem>"
|
||||
Items="_groups.Select((data,index)=> GetGroupRowData(data,index,level))"
|
||||
ChildContent="bodyRow()"></ForeachLoop>;
|
||||
Items="_groups.Select((data,index)=> GetGroupRowData(data,index,level, parentRowData?.Children ))"
|
||||
ChildContent="bodyRow()"></ForeachLoop>;
|
||||
|
||||
}
|
||||
#if NET5_0_OR_GREATER
|
||||
else if (EnableVirtualization) {
|
||||
else if (EnableVirtualization)
|
||||
{
|
||||
return @<Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize TItem="RowData<TItem>"
|
||||
OverscanCount="10"
|
||||
Items="showItems.Select((data, index) => GetRowData(data, index, level)).ToList()"
|
||||
Items="showItems.Select((data, index) => GetRowData(data, index, level, parentRowData?.Children )).ToList()"
|
||||
ItemContent="bodyRow()">
|
||||
</Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize>;
|
||||
}
|
||||
@ -255,9 +259,9 @@ RenderFragment<(Table<TItem> table, bool header)> colGroup = ctx =>
|
||||
{
|
||||
{ @*Build will fail without this block*@ }
|
||||
return
|
||||
@<ForeachLoop TItem="RowData<TItem>"
|
||||
Items="showItems.Select((data, index) => GetRowData(data, index, level))"
|
||||
ChildContent="bodyRow()"/>;
|
||||
@<ForeachLoop TItem="RowData<TItem>"
|
||||
Items="showItems.Select((data, index) => GetRowData(data, index, level, parentRowData?.Children))"
|
||||
ChildContent="bodyRow()" />;
|
||||
}
|
||||
|
||||
return builder => { };
|
||||
@ -267,8 +271,6 @@ RenderFragment<(Table<TItem> table, bool header)> colGroup = ctx =>
|
||||
{
|
||||
return currentRowData =>
|
||||
{
|
||||
//var currentRowData = GetRowData(tuple.data, tuple.index, level);
|
||||
|
||||
return @<TableRowWrapper RowData="currentRowData">
|
||||
@{
|
||||
var currentDataItem = currentRowData.DataItem;
|
||||
@ -289,7 +291,7 @@ RenderFragment<(Table<TItem> table, bool header)> colGroup = ctx =>
|
||||
}
|
||||
else
|
||||
{
|
||||
<TableRow @key="currentDataItem.Data">
|
||||
<TableRow>
|
||||
@ChildContent(currentDataItem.Data)
|
||||
</TableRow>
|
||||
}
|
||||
@ -297,9 +299,11 @@ RenderFragment<(Table<TItem> table, bool header)> colGroup = ctx =>
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
|
||||
// When expand button was clicked, would trigger here to add one more child level
|
||||
@if (currentDataItem.HasChildren && currentRowData.Expanded)
|
||||
{
|
||||
@body(SortFilterChildren(currentDataItem.Children), currentRowData.Level + 1);
|
||||
currentRowData.Children??=new();
|
||||
@body(SortFilterChildren(currentDataItem.Children), currentRowData);
|
||||
}
|
||||
@if (!currentDataItem.HasChildren && ExpandTemplate != null && RowExpandable(currentRowData) && currentRowData.Expanded)
|
||||
{
|
||||
|
@ -7,8 +7,8 @@ namespace AntDesign
|
||||
{
|
||||
public partial class Table<TItem> : ITable
|
||||
{
|
||||
private Dictionary<TItem, TableDataItem<TItem>> _dataSourceCache;
|
||||
private Dictionary<TItem, RowData<TItem>> _rootRowDataCache;
|
||||
private Dictionary<int, TableDataItem<TItem>> _dataSourceCache;
|
||||
private Dictionary<int, RowData<TItem>> _rootRowDataCache;
|
||||
|
||||
private void FlushCache()
|
||||
{
|
||||
@ -23,5 +23,80 @@ namespace AntDesign
|
||||
|
||||
_selection?.StateHasChanged();
|
||||
}
|
||||
|
||||
private RowData<TItem> GetGroupRowData(IGrouping<object, TItem> grouping, int index, int level, Dictionary<int, RowData<TItem>> rowCache = null)
|
||||
{
|
||||
int rowIndex = index + 1;
|
||||
|
||||
if (level == 0)
|
||||
{
|
||||
rowIndex += PageSize * (PageIndex - 1);
|
||||
}
|
||||
|
||||
var groupRowData = new RowData<TItem>()
|
||||
{
|
||||
Key = grouping.Key.ToString(),
|
||||
IsGrouping = true,
|
||||
RowIndex = rowIndex,
|
||||
DataItem = new TableDataItem<TItem>
|
||||
{
|
||||
HasChildren = true,
|
||||
Table = this,
|
||||
Children = grouping
|
||||
},
|
||||
Children = grouping.Select((data, index) => GetRowData(data, index, level, rowCache)).ToDictionary(x => GetHashCode(x.Data), x => x)
|
||||
};
|
||||
|
||||
return groupRowData;
|
||||
}
|
||||
|
||||
private RowData<TItem> GetRowData(TItem data, int index, int level, Dictionary<int, RowData<TItem>> rowCache = null)
|
||||
{
|
||||
int rowIndex = index + 1;
|
||||
|
||||
if (level == 0)
|
||||
{
|
||||
rowIndex += PageSize * (PageIndex - 1);
|
||||
}
|
||||
|
||||
var dataHashCode = GetHashCode(data);
|
||||
|
||||
if (!_dataSourceCache.TryGetValue(dataHashCode, out var currentDataItem) || currentDataItem == null)
|
||||
{
|
||||
currentDataItem = new TableDataItem<TItem>(data, this);
|
||||
currentDataItem.SetSelected(SelectedRows.Contains(data), triggersSelectedChanged: false);
|
||||
_dataSourceCache.Add(dataHashCode, currentDataItem);
|
||||
}
|
||||
|
||||
// this row cache may be for children rows
|
||||
rowCache ??= _rootRowDataCache;
|
||||
|
||||
if (!rowCache.TryGetValue(dataHashCode, out var currentRowData) || currentRowData == null)
|
||||
{
|
||||
currentRowData = new RowData<TItem>(currentDataItem)
|
||||
{
|
||||
Expanded = DefaultExpandAllRows && level < DefaultExpandMaxLevel
|
||||
};
|
||||
rowCache.Add(dataHashCode, currentRowData);
|
||||
}
|
||||
|
||||
currentRowData.Level = level;
|
||||
currentRowData.RowIndex = rowIndex;
|
||||
currentRowData.PageIndex = PageIndex;
|
||||
|
||||
if (currentDataItem.HasChildren && (level < DefaultExpandMaxLevel || currentRowData.Expanded))
|
||||
{
|
||||
foreach (var (item, i) in currentDataItem.Children.Select((item, index) => (item, index)))
|
||||
{
|
||||
currentRowData.Children ??= [];
|
||||
if (currentRowData.Children.ContainsKey(GetHashCode(item)))
|
||||
continue;
|
||||
|
||||
GetRowData(item, i, level + 1, currentRowData.Children);
|
||||
}
|
||||
}
|
||||
|
||||
return currentRowData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ namespace AntDesign
|
||||
{
|
||||
private IEnumerable<TItem> _outerSelectedRows;
|
||||
|
||||
/// <summary>
|
||||
/// Selected rows across pages
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public IEnumerable<TItem> SelectedRows
|
||||
{
|
||||
@ -25,16 +28,11 @@ namespace AntDesign
|
||||
public EventCallback<IEnumerable<TItem>> SelectedRowsChanged { get; set; }
|
||||
|
||||
private ISelectionColumn _selection;
|
||||
private readonly HashSet<TItem> _selectedRows = new();
|
||||
private readonly HashSet<TItem> _selectedRows;
|
||||
private bool _preventRowDataTriggerSelectedRowsChanged;
|
||||
|
||||
internal void DataItemSelectedChanged(TableDataItem<TItem> dataItem, bool selected)
|
||||
{
|
||||
if (!RowSelectable(dataItem.Data))
|
||||
{
|
||||
dataItem.SetSelected(!selected, triggersSelectedChanged: false);
|
||||
return;
|
||||
}
|
||||
if (selected)
|
||||
{
|
||||
_selectedRows.Add(dataItem.Data);
|
||||
@ -43,11 +41,9 @@ namespace AntDesign
|
||||
{
|
||||
_selectedRows.Remove(dataItem.Data);
|
||||
}
|
||||
if (!_preventRowDataTriggerSelectedRowsChanged)
|
||||
{
|
||||
SelectionChanged();
|
||||
_selection?.StateHasChanged();
|
||||
}
|
||||
|
||||
SelectionChanged();
|
||||
_selection?.StateHasChanged();
|
||||
}
|
||||
|
||||
ISelectionColumn ITable.Selection
|
||||
@ -56,21 +52,23 @@ namespace AntDesign
|
||||
set => _selection = value;
|
||||
}
|
||||
|
||||
bool ITable.AllSelected => _selectedRows.Count != 0 && _selectedRows.Count == GetAllItemsByTopLevelItems(_showItems, true).Count();
|
||||
bool ITable.AllSelected => _selectedRows.Any() && _dataSourceCache.Values.All(x => x.Disabled || x.Selected);
|
||||
|
||||
bool ITable.AnySelected => _selectedRows.Count > 0;
|
||||
bool ITable.AnySelected => _dataSourceCache.Values.Any(x => !x.Disabled && x.Selected);
|
||||
|
||||
public void SelectAll()
|
||||
{
|
||||
_selectedRows.Clear();
|
||||
foreach (var selectedRow in GetAllItemsByTopLevelItems(_showItems, true))
|
||||
_preventRowDataTriggerSelectedRowsChanged = true;
|
||||
|
||||
foreach (var select in _dataSourceCache.Values)
|
||||
{
|
||||
_selectedRows.Add(selectedRow);
|
||||
}
|
||||
foreach (TableDataItem<TItem> dataItem in _dataSourceCache.Values)
|
||||
{
|
||||
dataItem.SetSelected(_selectedRows.Contains(dataItem.Data));
|
||||
if (select.Disabled)
|
||||
continue;
|
||||
|
||||
select.SetSelected(true);
|
||||
_selectedRows.Add(select.Data);
|
||||
}
|
||||
_preventRowDataTriggerSelectedRowsChanged = false;
|
||||
|
||||
_selection?.StateHasChanged();
|
||||
SelectionChanged();
|
||||
@ -78,17 +76,21 @@ namespace AntDesign
|
||||
|
||||
private void ClearSelectedRows()
|
||||
{
|
||||
_selectedRows.Clear();
|
||||
foreach (TableDataItem<TItem> dataItem in _dataSourceCache.Values)
|
||||
{
|
||||
dataItem.SetSelected(false);
|
||||
_selectedRows.Remove(dataItem.Data);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnselectAll()
|
||||
{
|
||||
_preventRowDataTriggerSelectedRowsChanged = true;
|
||||
|
||||
ClearSelectedRows();
|
||||
|
||||
_preventRowDataTriggerSelectedRowsChanged = false;
|
||||
|
||||
_selection?.StateHasChanged();
|
||||
SelectionChanged();
|
||||
}
|
||||
@ -99,14 +101,15 @@ namespace AntDesign
|
||||
/// </summary>
|
||||
public void SetSelection(ICollection<string> keys)
|
||||
{
|
||||
_preventRowDataTriggerSelectedRowsChanged = true;
|
||||
|
||||
ClearSelectedRows();
|
||||
if (keys?.Count > 0)
|
||||
{
|
||||
_preventRowDataTriggerSelectedRowsChanged = true;
|
||||
_selection?.RowSelections.ForEach(x => x.RowData.Selected = keys.Contains(x.Key));
|
||||
_preventRowDataTriggerSelectedRowsChanged = false;
|
||||
_selection?.RowSelections.ForEach(x => x.RowData.SetSelected(keys.Contains(x.Key), x.CheckStrictly));
|
||||
}
|
||||
|
||||
_preventRowDataTriggerSelectedRowsChanged = false;
|
||||
_selection?.StateHasChanged();
|
||||
SelectionChanged();
|
||||
}
|
||||
@ -114,10 +117,11 @@ namespace AntDesign
|
||||
// Only select the given row (for radio selection)
|
||||
void ITable.SetSelection(ISelectionColumn selectItem)
|
||||
{
|
||||
ClearSelectedRows();
|
||||
|
||||
_preventRowDataTriggerSelectedRowsChanged = true;
|
||||
selectItem.RowData.Selected = true;
|
||||
|
||||
ClearSelectedRows();
|
||||
selectItem.RowData.SetSelected(true, selectItem.CheckStrictly);
|
||||
|
||||
_preventRowDataTriggerSelectedRowsChanged = false;
|
||||
|
||||
_selection?.StateHasChanged();
|
||||
@ -126,19 +130,20 @@ namespace AntDesign
|
||||
|
||||
private void SelectItem(TItem item)
|
||||
{
|
||||
if (!RowSelectable(item))
|
||||
return;
|
||||
_preventRowDataTriggerSelectedRowsChanged = true;
|
||||
|
||||
_selectedRows.Add(item);
|
||||
if (_dataSourceCache.TryGetValue(item, out var rowData))
|
||||
if (_dataSourceCache.TryGetValue(GetHashCode(item), out var rowData))
|
||||
{
|
||||
rowData.SetSelected(true);
|
||||
}
|
||||
|
||||
_preventRowDataTriggerSelectedRowsChanged = false;
|
||||
}
|
||||
|
||||
public void SetSelection(IEnumerable<TItem> items)
|
||||
{
|
||||
if (ReferenceEquals(items, _selectedRows))
|
||||
if (items.SequenceEqual(_selectedRows, this))
|
||||
return;
|
||||
|
||||
if (items is not null and not IReadOnlyCollection<TItem>)
|
||||
@ -146,41 +151,35 @@ namespace AntDesign
|
||||
// (which would happen when the given enumerable is based on _selectedRows with linq methods)
|
||||
items = items.ToArray();
|
||||
|
||||
_preventRowDataTriggerSelectedRowsChanged = true;
|
||||
ClearSelectedRows();
|
||||
items?.ForEach(SelectItem);
|
||||
|
||||
_selection?.StateHasChanged();
|
||||
SelectionChanged();
|
||||
|
||||
_preventRowDataTriggerSelectedRowsChanged = false;
|
||||
}
|
||||
|
||||
public void SetSelection(TItem item)
|
||||
{
|
||||
_preventRowDataTriggerSelectedRowsChanged = true;
|
||||
|
||||
ClearSelectedRows();
|
||||
if (item != null)
|
||||
{
|
||||
SelectItem(item);
|
||||
}
|
||||
_preventRowDataTriggerSelectedRowsChanged = false;
|
||||
|
||||
_selection?.StateHasChanged();
|
||||
SelectionChanged();
|
||||
}
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
[MemberNotNull(nameof(_selection))]
|
||||
#endif
|
||||
private void EnsureSelection()
|
||||
{
|
||||
if (_selection == null)
|
||||
{
|
||||
throw new InvalidOperationException("To use the SetSelection method for a table, you should add a Selection component to the column definition.");
|
||||
}
|
||||
}
|
||||
|
||||
void ITable.SelectionChanged() => SelectionChanged();
|
||||
//void ITable.SelectionChanged() => SelectionChanged();
|
||||
|
||||
private void SelectionChanged()
|
||||
{
|
||||
if (SelectedRowsChanged.HasDelegate)
|
||||
if (SelectedRowsChanged.HasDelegate && !_preventRowDataTriggerSelectedRowsChanged)
|
||||
{
|
||||
_preventRender = true;
|
||||
_outerSelectedRows = _selectedRows;
|
||||
|
@ -83,9 +83,6 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public Func<RowData<TItem>, bool> RowExpandable { get; set; } = _ => true;
|
||||
|
||||
[Parameter]
|
||||
public Func<TItem, bool> RowSelectable { get; set; } = _ => true;
|
||||
|
||||
[Parameter]
|
||||
public Func<TItem, IEnumerable<TItem>> TreeChildren { get; set; } = _ => Enumerable.Empty<TItem>();
|
||||
|
||||
@ -273,8 +270,9 @@ namespace AntDesign
|
||||
|
||||
public Table()
|
||||
{
|
||||
_dataSourceCache = new(this);
|
||||
_rootRowDataCache = new(this);
|
||||
_dataSourceCache = new();
|
||||
_rootRowDataCache = new();
|
||||
_selectedRows = new(this);
|
||||
}
|
||||
|
||||
private List<IFieldColumn> _groupedColumns = [];
|
||||
@ -473,7 +471,7 @@ namespace AntDesign
|
||||
|
||||
if (ServerSide)
|
||||
{
|
||||
_showItems = _dataSource;
|
||||
_showItems = _dataSource ?? Enumerable.Empty<TItem>();
|
||||
_total = Total;
|
||||
}
|
||||
else
|
||||
@ -510,16 +508,6 @@ namespace AntDesign
|
||||
{
|
||||
SetSelection(_outerSelectedRows);
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedRows?.Clear();
|
||||
}
|
||||
|
||||
var removedCacheItems = _dataSourceCache.Keys.Except(_showItems).ToArray();
|
||||
foreach (var item in removedCacheItems)
|
||||
{
|
||||
_dataSourceCache.Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
_treeMode = (TreeChildren != null && (_showItems?.Any(x => TreeChildren(x)?.Any() == true) == true)) || _groupedColumns.Count > 0;
|
||||
@ -655,33 +643,6 @@ namespace AntDesign
|
||||
FieldFilterTypeResolver ??= InjectedFieldFilterTypeResolver;
|
||||
}
|
||||
|
||||
private IEnumerable<TItem> GetAllItemsByTopLevelItems(IEnumerable<TItem> items, bool onlySelectable = false)
|
||||
{
|
||||
if (items?.Any() != true) return Array.Empty<TItem>();
|
||||
if (TreeChildren != null)
|
||||
{
|
||||
var itemsSet = new HashSet<TItem>();
|
||||
AddAllItemsAndChildren(items);
|
||||
items = itemsSet;
|
||||
|
||||
void AddAllItemsAndChildren(IEnumerable<TItem> itemsToAdd)
|
||||
{
|
||||
if (itemsToAdd is null)
|
||||
return;
|
||||
foreach (TItem item in itemsToAdd)
|
||||
{
|
||||
if (!itemsSet.Add(item))
|
||||
continue;
|
||||
|
||||
AddAllItemsAndChildren(TreeChildren(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onlySelectable) items = items.Where(x => RowSelectable(x));
|
||||
return items;
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
@ -863,76 +824,14 @@ namespace AntDesign
|
||||
return RowKey(x).Equals(RowKey(y));
|
||||
}
|
||||
|
||||
int IEqualityComparer<TItem>.GetHashCode(TItem obj)
|
||||
int IEqualityComparer<TItem>.GetHashCode(TItem obj) => GetHashCode(obj);
|
||||
|
||||
private int GetHashCode(TItem obj)
|
||||
{
|
||||
if (RowKey == null)
|
||||
RowKey = data => data;
|
||||
|
||||
return RowKey(obj).GetHashCode();
|
||||
}
|
||||
|
||||
private RowData<TItem> GetGroupRowData(IGrouping<object, TItem> grouping, int index, int level)
|
||||
{
|
||||
var groupRowData = new RowData<TItem>()
|
||||
{
|
||||
Key = grouping.Key.ToString(),
|
||||
IsGrouping = true,
|
||||
DataItem = new TableDataItem<TItem>
|
||||
{
|
||||
HasChildren = true,
|
||||
Table = this,
|
||||
Children = grouping
|
||||
},
|
||||
Children = grouping.Select((data, index) => GetRowData(data, index, level)).ToDictionary(x => x.Data, x => x)
|
||||
};
|
||||
|
||||
groupRowData.DataItem.RowData = groupRowData;
|
||||
return groupRowData;
|
||||
}
|
||||
|
||||
private RowData<TItem> GetRowData(TItem data, int index, int level)
|
||||
{
|
||||
int rowIndex;
|
||||
if (level == 0)
|
||||
{
|
||||
rowIndex = PageSize * (PageIndex - 1) + index + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
rowIndex = index + 1;
|
||||
}
|
||||
|
||||
if (!_dataSourceCache.TryGetValue(data, out var currentDataItem) || currentDataItem == null)
|
||||
{
|
||||
currentDataItem = new TableDataItem<TItem>(data, this);
|
||||
currentDataItem.SetSelected(SelectedRows.Contains(data), triggersSelectedChanged: false);
|
||||
_dataSourceCache.Add(data, currentDataItem);
|
||||
}
|
||||
|
||||
// this row cache may be for children rows
|
||||
var rowCache = currentDataItem.RowData?.Children ?? _rootRowDataCache;
|
||||
|
||||
if (rowCache!.TryGetValue(data, out var currentRowData) || currentRowData == null)
|
||||
{
|
||||
currentRowData = new RowData<TItem>(currentDataItem)
|
||||
{
|
||||
Expanded = DefaultExpandAllRows && level < DefaultExpandMaxLevel
|
||||
};
|
||||
rowCache[data] = currentRowData;
|
||||
|
||||
currentDataItem.RowData ??= currentRowData;
|
||||
}
|
||||
|
||||
if (currentDataItem.HasChildren && currentRowData.Expanded)
|
||||
{
|
||||
currentRowData.Children = new(this);
|
||||
}
|
||||
|
||||
currentRowData.Level = level;
|
||||
currentRowData.RowIndex = rowIndex;
|
||||
currentRowData.PageIndex = PageIndex;
|
||||
|
||||
return currentRowData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace AntDesign.TableModels
|
||||
/// <summary>
|
||||
/// hold the state of children rows
|
||||
/// </summary>
|
||||
public Dictionary<TItem, RowData<TItem>> Children { get; set; }
|
||||
public Dictionary<int, RowData<TItem>> Children { get; set; }
|
||||
|
||||
public RowData()
|
||||
{ }
|
||||
@ -28,6 +28,17 @@ namespace AntDesign.TableModels
|
||||
{
|
||||
DataItem = dataItem;
|
||||
}
|
||||
|
||||
protected override void CheckedChildren(bool isSelected, bool checkStrictly)
|
||||
{
|
||||
if (Children?.Any() != true)
|
||||
return;
|
||||
|
||||
foreach (var item in Children)
|
||||
{
|
||||
item.Value.SetSelected(isSelected, checkStrictly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -63,7 +74,7 @@ namespace AntDesign.TableModels
|
||||
|
||||
public abstract TableDataItem TableDataItem { get; }
|
||||
|
||||
public bool Selected { get => TableDataItem.Selected; set => TableDataItem.Selected = value; }
|
||||
public bool Selected { get => TableDataItem.Selected; }
|
||||
|
||||
public event Action<RowData, bool> ExpandedChanged;
|
||||
|
||||
@ -71,6 +82,18 @@ namespace AntDesign.TableModels
|
||||
{
|
||||
_expanded = expanded;
|
||||
}
|
||||
|
||||
protected abstract void CheckedChildren(bool isSelected, bool checkStrictly);
|
||||
|
||||
internal void SetSelected(bool isSelected, bool checkStrictly)
|
||||
{
|
||||
TableDataItem.SetSelected(isSelected);
|
||||
|
||||
if (checkStrictly)
|
||||
{
|
||||
CheckedChildren(isSelected, checkStrictly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -82,8 +105,6 @@ namespace AntDesign.TableModels
|
||||
|
||||
public IEnumerable<TItem> Children { get; set; }
|
||||
|
||||
public RowData<TItem> RowData { get; set; }
|
||||
|
||||
public TableDataItem()
|
||||
{
|
||||
}
|
||||
@ -98,7 +119,6 @@ namespace AntDesign.TableModels
|
||||
|
||||
protected override void OnSelectedChanged(bool value)
|
||||
{
|
||||
base.OnSelectedChanged(value);
|
||||
Table.DataItemSelectedChanged(this, value);
|
||||
}
|
||||
}
|
||||
@ -111,30 +131,22 @@ namespace AntDesign.TableModels
|
||||
/// <br/>
|
||||
/// For row specific data, see <see cref="RowData"/>.
|
||||
/// </summary>
|
||||
public class TableDataItem
|
||||
public abstract class TableDataItem
|
||||
{
|
||||
private bool _selected;
|
||||
|
||||
public bool Selected
|
||||
{
|
||||
get => _selected;
|
||||
set
|
||||
{
|
||||
if (_selected != value)
|
||||
{
|
||||
OnSelectedChanged(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public virtual bool HasChildren { get; set; }
|
||||
|
||||
public event Action<TableDataItem, bool> SelectedChanged;
|
||||
|
||||
protected virtual void OnSelectedChanged(bool value)
|
||||
{
|
||||
SetSelected(value);
|
||||
}
|
||||
protected abstract void OnSelectedChanged(bool value);
|
||||
|
||||
internal void SetSelected(bool selected, bool triggersSelectedChanged = true)
|
||||
{
|
||||
@ -144,6 +156,9 @@ namespace AntDesign.TableModels
|
||||
}
|
||||
|
||||
_selected = selected;
|
||||
|
||||
OnSelectedChanged(_selected);
|
||||
|
||||
if (triggersSelectedChanged)
|
||||
SelectedChanged?.Invoke(this, _selected);
|
||||
}
|
||||
|
@ -9,7 +9,9 @@
|
||||
@bind-PageSize="_pageSize"
|
||||
@bind-SelectedRows="selectedRows"
|
||||
OnChange="OnChange"
|
||||
Size="TableSize.Small">
|
||||
Size="TableSize.Small"
|
||||
RowKey="x=>x.Id"
|
||||
>
|
||||
<Selection Key="@(context.Id.ToString())" />
|
||||
<PropertyColumn Property="c=>c.Id" Sortable />
|
||||
<PropertyColumn Property="c=>c.Date" Format="yyyy-MM-dd" Sortable />
|
||||
|
@ -16,7 +16,7 @@
|
||||
}
|
||||
</h5>
|
||||
|
||||
<Table @ref="table" DataSource="@data" @bind-SelectedRows="selectedRows" RowSelectable="@(x => x.Name!="Disabled User")" RowKey="x=>x.Name">
|
||||
<Table @ref="table" DataSource="@data" @bind-SelectedRows="selectedRows" RowKey="x=>x.Name">
|
||||
<Selection Key="@context.Name" Type="@selectionType" Disabled="@(context.Name == "Disabled User")" />
|
||||
<PropertyColumn Property="c=>c.Name">
|
||||
<a>@context.Name</a>
|
||||
@ -26,7 +26,6 @@
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
|
||||
@code {
|
||||
|
||||
ITable table;
|
||||
@ -73,7 +72,6 @@
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
selectedRows = data[..1];
|
||||
}
|
||||
|
||||
public void RemoveSelection(string key)
|
||||
|
@ -1,7 +1,7 @@
|
||||
@using System.ComponentModel
|
||||
|
||||
<Table DataSource="_data" TreeChildren="item=>item.Children" DefaultExpandAllRows Size="TableSize.Small">
|
||||
<Selection CheckStrictly />
|
||||
<Selection />
|
||||
<PropertyColumn Property="c=>c.Name" />
|
||||
<PropertyColumn Property="c=>c.Age" Width="12%" Sortable />
|
||||
<PropertyColumn Property="c=>c.Address" Width="30%" />
|
||||
|
@ -27,7 +27,8 @@
|
||||
OnChange="OnChange"
|
||||
Total="total"
|
||||
RemoteDataSource
|
||||
Bordered>
|
||||
Bordered
|
||||
RowKey="x=>x.Name">
|
||||
<Selection />
|
||||
<PropertyColumn Property="c=>c.Name" Width="400" />
|
||||
<PropertyColumn Property="c=>c.Age" Width="150" />
|
||||
|
Loading…
Reference in New Issue
Block a user