Transitioned PDF rendering in `EnvelopeReceiverPage.razor` from `PDF.js` to `DevExpress DxPdfViewer`. Updated code and documentation to reflect the verified API of `DevExpress.Blazor.PdfViewer` v25.2.3, addressing its limitations (e.g., lack of `GoToPageAsync`, `PageNumberChanged`). Implemented `CustomizeToolbar` for navigation/zoom controls and manual state tracking for `_currentPage` and `_viewerZoomLevel`. Replaced JavaScript interop for page count with the `PageCount` property. Retained the custom thumbnail sidebar due to API constraints. Added temporary debug tools for DOM analysis and navigation testing. Updated `TESTING_CHECKLIST.md` and added `DEVEXPRESS_V25_LIMITATIONS.md` to document the new strategy, API limitations, and testing scenarios. Cross-page signature navigation implemented with state updates, though visible page changes remain manual. Prepared for future improvements while ensuring functional migration to `DxPdfViewer`.
7.2 KiB
7.2 KiB
DevExpress Blazor PdfViewer v25.2.3 - Verified API Reference
Source: All information in this document has been verified from the actual source code of
DevExpress.Blazor.PdfViewerv25.2.3 package. AI-generated API suggestions (GoToPageAsync, PageNumberChanged, etc.) are NOT real – do not use them.
Verified Available Parameters
| Property | Type | Access | Default | Description |
|---|---|---|---|---|
DocumentContent |
byte[] |
[Parameter] GET/SET |
– | Feeds PDF content as byte array |
CssClass |
string |
[Parameter] GET/SET |
– | Assigns CSS class to component |
DocumentName |
string |
[Parameter] GET/SET |
"Document" |
Download filename |
IsSinglePagePreview |
bool |
[Parameter] GET/SET |
false |
Single page mode |
SizeMode |
SizeMode? |
[Parameter] GET/SET |
null |
Small, Medium, Large |
ZoomLevel |
double |
[Parameter] GET/SET |
-1 |
Factor (not percentage). 1.5 = 150% |
ActivePageIndex |
int |
GET only | – | Active page index (0-based). No SET. |
PageCount |
int |
GET only | – | Total page count in document |
Missing Events (NOT AVAILABLE in v25.2.3)
PageNumberChanged– Not availableZoomLevelChanged– Not available- User scrolling or native toolbar page changes do not trigger C# code
Missing Properties (NOT AVAILABLE in v25.2.3)
ToolbarVisible– Not available (toolbar cannot be completely hidden)ActivePageIndex(settable) – Read-only; no programmatic page navigation
Missing Methods (NOT AVAILABLE in v25.2.3)
GoToPageAsync()– Not availableGoToNextPageAsync()– Not availableZoomAsync()– Not available
Available Event
CustomizeToolbar– Only available event for toolbar customization
Critical Integration Notes
ZoomLevel takes factor, not percentage
// CORRECT
_viewerZoomLevel = 1.5; // viewer displays "150%"
_viewerZoomLevel = _currentZoom / 100d; // _currentZoom=150 -> 1.5
// WRONG
_viewerZoomLevel = 150; // viewer displays "15000%"
PageCount replaces JS call
// CORRECT - read directly from component (no JS needed)
_totalPages = _pdfViewer.PageCount;
// OLD method (no longer needed for this purpose)
// _totalPages = await JSRuntime.InvokeAsync<int>("pdfViewer.getTotalPages");
ActivePageIndex is read-only
// CORRECT - read for state synchronization
var currentPage = _pdfViewer.ActivePageIndex + 1; // convert to 1-based
// COMPILE ERROR - no setter
// _pdfViewer.ActivePageIndex = 3; // COMPILE ERROR
DocumentContent byte[] feeding
<DxPdfViewer @ref="_pdfViewer"
DocumentContent="@_pdfDocumentContent"
ZoomLevel="@_viewerZoomLevel"
IsSinglePagePreview="true" />
@code {
DxPdfViewer? _pdfViewer;
byte[]? _pdfDocumentContent; // populate in OnInitializedAsync
double _viewerZoomLevel = 1.5; // 150%
}
Impact on EnvelopeReceiverPage
Features That Don't Work
- Event-driven overlay updates – No page/zoom change events
- Thumbnail click navigation – Cannot navigate viewer to specific page via C# API
- Cross-page signature navigation – No programmatic page change API
- Automatic overlay synchronization – User scroll/native toolbar doesn't trigger C#
Features That Work
- ZoomLevel binding – Custom zoom buttons can update viewer zoom
- PageCount – Total pages can be read directly from component
- IsSinglePagePreview – Single page mode works
- DocumentContent – byte[] feeding works perfectly
- CustomizeToolbar – Only way to add custom buttons to toolbar
Workaround Strategy
CustomizeToolbar event is used to add custom navigation/zoom buttons.
Manual state tracking (_currentPage, _currentZoom, _viewerZoomLevel) is kept in C#.
Overlay refresh is manually triggered only after button clicks.
protected void OnCustomizeToolbar(ToolbarModel toolbarModel)
{
toolbarModel.AllItems.Clear();
var prevButton = new ToolbarItem
{
Text = "Previous",
IconCssClass = "dx-icon-chevronprev",
Enabled = _currentPage > 1,
Click = async (args) =>
{
if (_currentPage > 1)
{
_currentPage--;
_viewerZoomLevel = _currentZoom / 100d;
await InvokeAsync(StateHasChanged);
await RenderSignatureButtonsAsync();
}
}
};
var nextButton = new ToolbarItem
{
Text = "Next",
IconCssClass = "dx-icon-chevronnext",
Enabled = _currentPage < _totalPages,
Click = async (args) =>
{
if (_currentPage < _totalPages)
{
_currentPage++;
_viewerZoomLevel = _currentZoom / 100d;
await InvokeAsync(StateHasChanged);
await RenderSignatureButtonsAsync();
}
}
};
var zoomInButton = new ToolbarItem
{
IconCssClass = "dx-icon-plus",
Enabled = _currentZoom < 300,
Click = async (args) =>
{
_currentZoom = Math.Min(_currentZoom + 10, 300);
_viewerZoomLevel = _currentZoom / 100d; // 150 -> 1.5
await InvokeAsync(StateHasChanged);
await RenderSignatureButtonsAsync();
}
};
var zoomOutButton = new ToolbarItem
{
IconCssClass = "dx-icon-minus",
Enabled = _currentZoom > 50,
Click = async (args) =>
{
_currentZoom = Math.Max(_currentZoom - 10, 50);
_viewerZoomLevel = _currentZoom / 100d; // 150 -> 1.5
await InvokeAsync(StateHasChanged);
await RenderSignatureButtonsAsync();
}
};
toolbarModel.AllItems.Add(prevButton);
toolbarModel.AllItems.Add(nextButton);
toolbarModel.AllItems.Add(zoomInButton);
toolbarModel.AllItems.Add(zoomOutButton);
}
PageCount reading example (in OnAfterRenderAsync)
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!_pdfLoaded && _pdfDocumentContent is { Length: > 0 })
{
await Task.Delay(300); // wait for DxPdfViewer to load
if (_pdfViewer is not null && _pdfViewer.PageCount > 0)
{
_totalPages = _pdfViewer.PageCount; // read directly instead of JS
_pdfLoaded = true;
await InvokeAsync(StateHasChanged);
await RenderThumbnailsAsync();
await RenderSignatureButtonsAsync();
}
}
}
Known Acceptable Limitations
- If user scrolls PDF, C#
_currentPagedoes not synchronize - Thumbnail clicks update state but cannot move DevExpress viewer to target page
- Browser zoom gestures do not trigger overlay updates
- Custom toolbar buttons correctly trigger overlay updates
References
- DevExpress official documentation: https://docs.devexpress.com/Blazor/DevExpress.Blazor.PdfViewer.DxPdfViewer
- Verified package:
DevExpress.Blazor.PdfViewerv25.2.3 - Note: AI-suggested APIs (GoToPageAsync, PageNumberChanged, ZoomLevelChanged, ZoomAsync, ToolbarVisible) are NOT real. Do not use.