diff --git a/Radzen.Blazor/Spreadsheet/ColumnHeader.razor b/Radzen.Blazor/Spreadsheet/ColumnHeader.razor
index 7c2de6eff..3c0af0034 100644
--- a/Radzen.Blazor/Spreadsheet/ColumnHeader.razor
+++ b/Radzen.Blazor/Spreadsheet/ColumnHeader.razor
@@ -1,6 +1,6 @@
@using Radzen.Documents.Spreadsheet
@using Microsoft.AspNetCore.Components.Web
-@inherits CellBase
+@inherits HeaderBase
@ColumnRef.ToString(Column)
diff --git a/Radzen.Blazor/Spreadsheet/ColumnHeader.razor.cs b/Radzen.Blazor/Spreadsheet/ColumnHeader.razor.cs
index 29617ef28..565d1102b 100644
--- a/Radzen.Blazor/Spreadsheet/ColumnHeader.razor.cs
+++ b/Radzen.Blazor/Spreadsheet/ColumnHeader.razor.cs
@@ -1,7 +1,6 @@
+using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;
using Radzen.Blazor.Rendering;
-using System;
-using System.Threading.Tasks;
using Radzen.Documents.Spreadsheet;
namespace Radzen.Blazor.Spreadsheet;
@@ -10,7 +9,7 @@ namespace Radzen.Blazor.Spreadsheet;
///
/// Renders a column header in a spreadsheet.
///
-public partial class ColumnHeader : CellBase, IDisposable
+public partial class ColumnHeader : HeaderBase
{
///
/// Gets or sets the column index of the column header.
@@ -18,95 +17,26 @@ public partial class ColumnHeader : CellBase, IDisposable
[Parameter]
public int Column { get; set; }
- ///
- /// Gets or sets the sheet that contains the column header.
- ///
- [Parameter]
- public Worksheet? Worksheet { get; set; }
+ ///
+ protected override int Index => Column;
- private bool active;
- private bool selected;
+ ///
+ protected override string IndexParameterName => nameof(Column);
+
+ ///
+ [SuppressMessage("Design", "CA1062", Justification = "Base class guarantees non-null.")]
+ protected override bool CheckIsActive(Selection selection) => selection.IsActive(new ColumnRef(Column));
+
+ ///
+ [SuppressMessage("Design", "CA1062", Justification = "Base class guarantees non-null.")]
+ protected override bool CheckIsSelected(Selection selection) => selection.IsSelected(new ColumnRef(Column));
private string Class => ClassList.Create("rz-spreadsheet-column-header")
.Add("rz-spreadsheet-frozen-row", FrozenState.HasFlag(FrozenState.Row))
.Add("rz-spreadsheet-frozen-column", FrozenState.HasFlag(FrozenState.Column))
- .Add("rz-spreadsheet-header-active", active)
- .Add("rz-spreadsheet-header-selected", selected)
+ .Add("rz-spreadsheet-header-active", Active)
+ .Add("rz-spreadsheet-header-selected", Selected)
.ToString();
private string ResizeHandleStyle => $"left: {Rect.Right.ToPx()}; top: {Rect.Top.ToPx()}; height: {Rect.Height.ToPx()};";
-
-
- ///
- protected override void OnInitialized()
- {
- UpdateState();
- }
-
- ///
- public override async Task SetParametersAsync(ParameterView parameters)
- {
- if (Worksheet != null)
- {
- Worksheet.Selection.Changed -= OnSelectionChanged;
- }
-
- var didColumnChange = parameters.TryGetValue(nameof(Column), out var column) && Column != column;
-
- await base.SetParametersAsync(parameters);
-
- if (Worksheet != null)
- {
- Worksheet.Selection.Changed += OnSelectionChanged;
- }
-
- if (didColumnChange)
- {
- UpdateState();
- }
- }
-
- private void OnSelectionChanged()
- {
- var dirty = UpdateState();
-
- if (dirty)
- {
- StateHasChanged();
- }
- }
-
- private bool UpdateState()
- {
- var dirty = false;
-
- if (Worksheet is not null)
- {
- var address = new ColumnRef(Column);
-
- if (Worksheet.Selection.IsActive(address) != active)
- {
- active = !active;
-
- dirty = true;
- }
-
- if (Worksheet.Selection.IsSelected(address) != selected)
- {
- selected = !selected;
-
- dirty = true;
- }
- }
-
- return dirty;
- }
-
- void IDisposable.Dispose()
- {
- if (Worksheet != null)
- {
- Worksheet.Selection.Changed -= OnSelectionChanged;
- }
- }
-}
\ No newline at end of file
+}
diff --git a/Radzen.Blazor/Spreadsheet/HeaderBase.cs b/Radzen.Blazor/Spreadsheet/HeaderBase.cs
new file mode 100644
index 000000000..35480ac07
--- /dev/null
+++ b/Radzen.Blazor/Spreadsheet/HeaderBase.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Components;
+using Radzen.Blazor.Rendering;
+
+using Radzen.Documents.Spreadsheet;
+namespace Radzen.Blazor.Spreadsheet;
+
+#nullable enable
+
+///
+/// Base class for row and column header components. Manages selection state tracking,
+/// event subscription lifecycle, and CSS class generation.
+/// Subclasses provide the axis-specific index, address creation, and CSS class name.
+///
+public abstract class HeaderBase : CellBase, IDisposable
+{
+ ///
+ /// Gets or sets the worksheet that contains this header.
+ ///
+ [Parameter]
+ public Worksheet? Worksheet { get; set; }
+
+ private bool active;
+
+ ///
+ /// Gets whether the header is in the active selection range.
+ ///
+ protected bool Active => active;
+
+ private bool selected;
+
+ ///
+ /// Gets whether the header's entire row/column is selected.
+ ///
+ protected bool Selected => selected;
+
+ ///
+ /// Gets the axis index (row or column) for this header.
+ ///
+ protected abstract int Index { get; }
+
+ ///
+ /// Gets the parameter name to watch for changes (e.g. "Row" or "Column").
+ ///
+ protected abstract string IndexParameterName { get; }
+
+ ///
+ /// Returns whether the selection is active for this header's axis position.
+ /// Called only after validating that is non-null.
+ ///
+ protected abstract bool CheckIsActive(Selection selection);
+
+ ///
+ /// Returns whether the selection covers this header's entire row/column.
+ /// Called only after validating that is non-null.
+ ///
+ protected abstract bool CheckIsSelected(Selection selection);
+
+ ///
+ protected override void OnInitialized()
+ {
+ UpdateState();
+ }
+
+ ///
+ public override async Task SetParametersAsync(ParameterView parameters)
+ {
+ if (Worksheet != null)
+ {
+ Worksheet.Selection.Changed -= OnSelectionChanged;
+ }
+
+ var didIndexChange = parameters.TryGetValue(IndexParameterName, out var index) && Index != index;
+
+ await base.SetParametersAsync(parameters);
+
+ if (Worksheet != null)
+ {
+ Worksheet.Selection.Changed += OnSelectionChanged;
+ }
+
+ if (didIndexChange)
+ {
+ UpdateState();
+ }
+ }
+
+ private void OnSelectionChanged()
+ {
+ if (UpdateState())
+ {
+ StateHasChanged();
+ }
+ }
+
+ private bool UpdateState()
+ {
+ var dirty = false;
+
+ if (Worksheet is not null)
+ {
+ var selection = Worksheet.Selection;
+
+ if (CheckIsActive(selection) != active)
+ {
+ active = !active;
+ dirty = true;
+ }
+
+ if (CheckIsSelected(selection) != selected)
+ {
+ selected = !selected;
+ dirty = true;
+ }
+ }
+
+ return dirty;
+ }
+
+ ///
+ void IDisposable.Dispose()
+ {
+ if (Worksheet != null)
+ {
+ Worksheet.Selection.Changed -= OnSelectionChanged;
+ }
+ }
+}
diff --git a/Radzen.Blazor/Spreadsheet/RowHeader.razor b/Radzen.Blazor/Spreadsheet/RowHeader.razor
index 075abdb0a..2e001d744 100644
--- a/Radzen.Blazor/Spreadsheet/RowHeader.razor
+++ b/Radzen.Blazor/Spreadsheet/RowHeader.razor
@@ -1,6 +1,6 @@
@using Radzen.Documents.Spreadsheet
@using Microsoft.AspNetCore.Components.Web
-@inherits CellBase
+@inherits HeaderBase
@(Row + 1)
diff --git a/Radzen.Blazor/Spreadsheet/RowHeader.razor.cs b/Radzen.Blazor/Spreadsheet/RowHeader.razor.cs
index 09d9d34fc..e8e134480 100644
--- a/Radzen.Blazor/Spreadsheet/RowHeader.razor.cs
+++ b/Radzen.Blazor/Spreadsheet/RowHeader.razor.cs
@@ -1,8 +1,6 @@
-
+using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;
using Radzen.Blazor.Rendering;
-using System;
-using System.Threading.Tasks;
using Radzen.Documents.Spreadsheet;
namespace Radzen.Blazor.Spreadsheet;
@@ -12,7 +10,7 @@ namespace Radzen.Blazor.Spreadsheet;
///
/// Renders a row header in a spreadsheet.
///
-public partial class RowHeader : CellBase, IDisposable
+public partial class RowHeader : HeaderBase
{
///
/// Gets or sets the row index for the row header.
@@ -20,93 +18,26 @@ public partial class RowHeader : CellBase, IDisposable
[Parameter]
public int Row { get; set; }
- ///
- /// Gets or sets the sheet that contains the row header.
- ///
- [Parameter]
- public Worksheet? Worksheet { get; set; }
+ ///
+ protected override int Index => Row;
- private bool active;
- private bool selected;
+ ///
+ protected override string IndexParameterName => nameof(Row);
+
+ ///
+ [SuppressMessage("Design", "CA1062", Justification = "Base class guarantees non-null.")]
+ protected override bool CheckIsActive(Selection selection) => selection.IsActive(new RowRef(Row));
+
+ ///
+ [SuppressMessage("Design", "CA1062", Justification = "Base class guarantees non-null.")]
+ protected override bool CheckIsSelected(Selection selection) => selection.IsSelected(new RowRef(Row));
private string Class => ClassList.Create("rz-spreadsheet-row-header")
.Add("rz-spreadsheet-frozen-row", FrozenState.HasFlag(FrozenState.Row))
.Add("rz-spreadsheet-frozen-column", FrozenState.HasFlag(FrozenState.Column))
- .Add("rz-spreadsheet-header-active", active)
- .Add("rz-spreadsheet-header-selected", selected)
+ .Add("rz-spreadsheet-header-active", Active)
+ .Add("rz-spreadsheet-header-selected", Selected)
.ToString();
private string ResizeHandleStyle => $"top: {Rect.Bottom.ToPx()}; left: {Rect.Left.ToPx()}; width: {Rect.Width.ToPx()};";
-
- ///
- protected override void OnInitialized()
- {
- UpdateState();
- }
-
- ///
- public override async Task SetParametersAsync(ParameterView parameters)
- {
- if (Worksheet != null)
- {
- Worksheet.Selection.Changed -= OnSelectionChanged;
- }
- var didRowChange = parameters.TryGetValue(nameof(Row), out var row) && Row != row;
-
- await base.SetParametersAsync(parameters);
-
- if (Worksheet != null)
- {
- Worksheet.Selection.Changed += OnSelectionChanged;
- }
-
- if (didRowChange)
- {
- UpdateState();
- }
- }
-
- private void OnSelectionChanged()
- {
- var dirty = UpdateState();
-
- if (dirty)
- {
- StateHasChanged();
- }
- }
-
- private bool UpdateState()
- {
- var dirty = false;
-
- if (Worksheet is not null)
- {
- var address = new RowRef(Row);
-
- if (Worksheet.Selection.IsActive(address) != active)
- {
- active = !active;
-
- dirty = true;
- }
-
- if (Worksheet.Selection.IsSelected(address) != selected)
- {
- selected = !selected;
-
- dirty = true;
- }
- }
-
- return dirty;
- }
-
- void IDisposable.Dispose()
- {
- if (Worksheet != null)
- {
- Worksheet.Selection.Changed -= OnSelectionChanged;
- }
- }
-}
\ No newline at end of file
+}