Site icon Blog ARC Optimizer

Exploration du contrôle de la visionneuse PDF Blazor


Dans cet article, vous apprendrez à afficher des documents PDF sur vos sites Web créés avec Blazor, grâce au contrôle de Telerik PDFViewer. Sans aucun doute, il s’agit d’une fonctionnalité très utile lorsque vous souhaitez que vos utilisateurs naviguent facilement dans les documents PDF d’intérêt, sans avoir besoin de les télécharger.

Commençons!

Qu’est-ce que le contrôle du Blazor PDF Viewer?

Le Visionneuse PDF Blazor Le contrôle est un progrès Telerik Ui pour le blazor Composant qui vous permet d’ouvrir des documents PDF dans les navigateurs des utilisateurs à partir de votre propre application. Un grand avantage de ce composant est qu’il offre des fonctionnalités telles que la pagination, le zoom, l’impression, la sélection de texte, la recherche, entre autres. Certains cas pratiques où vous pouvez utiliser ce composant comprennent:

  • Affichage des factures
  • Lire des documents derrière une passerelle de paiement
  • Aperçu des documents téléchargés
  • Manuels d’utilisation
  • Lire des documents qui font partie d’un projet
  • Entre autres

De plus, car il est possible d’utiliser le .Net Maui Blazor Hybrid modèle pour réutiliser les composants du blazor dans .net Maui, vous pouvez créer des solutions multiplateforme sans les maux de tête qui sont souvent livrés avec l’affichage des documents PDF dans les applications mobiles.

Travailler avec le composant Telerik PDFViewer

Voyons comment travailler avec le composant PDFViewer. Pour cela, supposons que nous mettons à jour un portail où nous permettrons aux utilisateurs de générer des factures d’achat.

Pour éviter les problèmes lors de l’utilisation du composant PDFViewer, assurez-vous de configurer votre projet en fonction du Guide d’installation des composants du blazor. Une fois que vous avez fait cela, créez un nouveau composant et remplacez son contenu par le code suivant:

@page "/invoice-generator"
@using System.Globalization
@inject HttpClient Http
@inject NavigationManager NavigationManager

<PageTitle>Invoice Generator</PageTitle>

<div class="container-fluid">
    <div class="row">
        <div class="col-12">
            <h1 class="mb-4 text-center">Invoice Generator</h1>
        </div>
    </div>
    @if (showPdfPlaceholder)
    {
        <div class="row">
            <div class="col-12">
                <div class="pdf-placeholder">
                    <div class="pdf-content">
                        <TelerikSvgIcon Icon="@SvgIcon.FilePdf" Size="@ThemeConstants.SvgIcon.Size.ExtraLarge" />
                        <h3 class="mt-3">PDF Invoice here</h3>
                        <p class="text-muted">Your generated invoice will appear here</p>
                    </div>
                </div>
            </div>
        </div>
    }
    else if (isGenerating)
    {
        <div class="row">
            <div class="col-12">
                <div class="loader-overlay">
                    <div class="loader-content">
                        <TelerikLoader Type="@LoaderType.Pulsing"
                                       Size="@ThemeConstants.Loader.Size.Large"
                                       ThemeColor="@ThemeConstants.Loader.ThemeColor.Primary" />
                        <h3 class="mt-4">Generating Invoice...</h3>
                        <p class="text-muted">Please wait while we create your PDF invoice</p>
                    </div>
                </div>
            </div>
        </div>
    }
    else
    {
        <div class="row justify-content-center">
            <div class="col-lg-8 col-xl-6">
                <div class="invoice-form-card">
                    <EditForm Model="@invoiceModel" OnValidSubmit="@OnValidSubmit" class="invoice-form">
                        <DataAnnotationsValidator />
                        <ValidationSummary />
                        
                        <div class="form-section">
                            <h4 class="section-title">Company Information</h4>
                            <div class="row g-3">
                                <div class="col-12">
                                    <label class="form-label">Company Name</label>
                                    <TelerikTextBox @bind-Value="@invoiceModel.CompanyName"
                                                    ReadOnly="true"
                                                    Class="readonly-field" />
                                </div>

                                <div class="col-12">
                                    <label class="form-label">Company Address</label>
                                    <TelerikTextArea @bind-Value="@invoiceModel.CompanyAddress"
                                                     ReadOnly="true"
                                                     Rows="2"
                                                     Class="readonly-field" />
                                </div>

                                <div class="col-md-6">
                                    <label class="form-label">Company Email</label>
                                    <TelerikTextBox @bind-Value="@invoiceModel.CompanyEmail"
                                                    ReadOnly="true"
                                                    Class="readonly-field" />
                                </div>

                                <div class="col-md-6">
                                    <label class="form-label">Company Phone</label>
                                    <TelerikTextBox @bind-Value="@invoiceModel.CompanyPhone"
                                                    ReadOnly="true"
                                                    Class="readonly-field" />
                                </div>
                            </div>
                        </div>
                        
                        <div class="form-section">
                            <h4 class="section-title">Invoice Details</h4>
                            <div class="row g-3">
                                <div class="col-md-6">
                                    <label class="form-label">Invoice Number</label>
                                    <TelerikTextBox @bind-Value="@invoiceModel.InvoiceNumber"
                                                    ReadOnly="true"
                                                    Class="readonly-field" />
                                </div>

                                <div class="col-md-6">
                                    <label class="form-label">Invoice Date</label>
                                    <TelerikDatePicker @bind-Value="@invoiceModel.InvoiceDate"
                                                       ReadOnly="true"
                                                       Class="readonly-field" />
                                </div>

                                <div class="col-md-6">
                                    <label class="form-label">Due Date</label>
                                    <TelerikDatePicker @bind-Value="@invoiceModel.DueDate"
                                                       ReadOnly="true"
                                                       Class="readonly-field" />
                                </div>
                            </div>
                        </div>
                        
                        <div class="form-section">
                            <h4 class="section-title">Customer Information</h4>
                            <div class="row g-3">
                                <div class="col-12">
                                    <label class="form-label">Customer Name *</label>
                                    <TelerikTextBox @bind-Value="@invoiceModel.CustomerName"
                                                    Placeholder="Enter customer name"
                                                    Class="editable-field" />
                                    <ValidationMessage For="@(() => invoiceModel.CustomerName)" />
                                </div>

                                <div class="col-md-6">
                                    <label class="form-label">Customer Email *</label>
                                    <TelerikTextBox @bind-Value="@invoiceModel.CustomerEmail"
                                                    Placeholder="customer@email.com"
                                                    Class="editable-field" />
                                    <ValidationMessage For="@(() => invoiceModel.CustomerEmail)" />
                                </div>

                                <div class="col-md-6">
                                    <label class="form-label">Customer Phone</label>
                                    <TelerikTextBox @bind-Value="@invoiceModel.CustomerPhone"
                                                    Placeholder="+1 (555) 123-4567"
                                                    Class="editable-field" />
                                </div>

                                <div class="col-12">
                                    <label class="form-label">Customer Address *</label>
                                    <TelerikTextArea @bind-Value="@invoiceModel.CustomerAddress"
                                                     Placeholder="Enter customer address"
                                                     Rows="2"
                                                     Class="editable-field" />
                                    <ValidationMessage For="@(() => invoiceModel.CustomerAddress)" />
                                </div>
                            </div>
                        </div>
                        
                        <div class="form-section">
                            <h4 class="section-title">Invoice Items</h4>
                            <div class="row g-3">
                                <div class="col-md-6">
                                    <label class="form-label">Item Description</label>
                                    <TelerikTextBox @bind-Value="@invoiceModel.ItemDescription"
                                                    ReadOnly="true"
                                                    Class="readonly-field" />
                                </div>

                                <div class="col-md-2">
                                    <label class="form-label">Quantity</label>
                                    <TelerikNumericTextBox @bind-Value="@invoiceModel.Quantity"
                                                           Min="1"
                                                           Max="999"
                                                           Class="editable-field" />
                                </div>                                <div class="col-md-2">
                                    <label class="form-label">Unit Price</label>
                                    <TelerikNumericTextBox @bind-Value="@invoiceModel.UnitPrice"
                                                           Min="0"
                                                           Format="$#,##0.00"
                                                           ReadOnly="true"
                                                           Class="readonly-field" />
                                </div><div class="col-md-2">
                                    <label class="form-label">Total Amount</label>
                                    <TelerikTextBox @bind-Value="@invoiceModel.TotalAmountDisplay"
                                                    ReadOnly="true"
                                                    Class="readonly-field total-field" />
                                </div>
                            </div>
                        </div>
                        
                        <div class="form-section">
                            <h4 class="section-title">Additional Information</h4>
                            <div class="row g-3">
                                <div class="col-12">
                                    <label class="form-label">Notes</label>
                                    <TelerikTextArea @bind-Value="@invoiceModel.Notes"
                                                     Placeholder="Add any additional notes or terms"
                                                     Rows="3"
                                                     Class="editable-field" />
                                </div>
                            </div>
                        </div>

                        <div class="text-center mt-4">
                            <TelerikButton ButtonType="@ButtonType.Submit"
                                           ThemeColor="@ThemeConstants.Button.ThemeColor.Primary"
                                           Size="@ThemeConstants.Button.Size.Large"
                                           Class="generate-btn"
                                           Enabled="@(!isGenerating)">
                                @if (isGenerating)
                                {
                                    <TelerikLoader Type="@LoaderType.Pulsing" Size="@ThemeConstants.Loader.Size.Small" />
                                    <span class="ms-2">Generating Invoice...</span>
                                }
                                else
                                {
                                    <TelerikSvgIcon Icon="@SvgIcon.FilePdf" />
                                    <span class="ms-2">Generate Invoice</span>
                                }
                            </TelerikButton>
                        </div>
                    </EditForm>
                </div>
            </div>
        </div>
    }
</div>

@code {
    private InvoiceModel invoiceModel = new();
    private bool isGenerating = false;
    private bool showPdfPlaceholder = false;
    protected override void OnInitialized()
    {        
        invoiceModel.CompanyName = "Demo Solutions Inc.";
        invoiceModel.CompanyAddress = "123 Business Street\nSuite 456\nNew York, NY 10001";
        invoiceModel.CompanyEmail = "info@demosolutions.com";
        invoiceModel.CompanyPhone = "+1 (555) 123-4567";

        invoiceModel.InvoiceNumber = $"INV-{DateTime.Now:yyyyMMdd}-001";
        invoiceModel.InvoiceDate = DateTime.Now;
        invoiceModel.DueDate = DateTime.Now.AddDays(30);

        invoiceModel.ItemDescription = "Professional Consulting Services";
        
        invoiceModel.SetUnitPrice(150.00m);
        invoiceModel.SetQuantity(10);

        invoiceModel.Notes = "Payment due within 30 days. Thank you for your business!";
    }
    private async Task OnValidSubmit()
    {
        isGenerating = true;
        StateHasChanged();
        
        await Task.Delay(3000);

        isGenerating = false;
        showPdfPlaceholder = true;
        StateHasChanged();
    }

    public class InvoiceModel
    {        
        public string CompanyName { get; set; } = string.Empty;
        public string CompanyAddress { get; set; } = string.Empty;
        public string CompanyEmail { get; set; } = string.Empty;
        public string CompanyPhone { get; set; } = string.Empty;
        
        public string InvoiceNumber { get; set; } = string.Empty;
        public DateTime InvoiceDate { get; set; }
        public DateTime DueDate { get; set; }
        
        [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Customer name is required")]
        [System.ComponentModel.DataAnnotations.StringLength(100, ErrorMessage = "Customer name cannot exceed 100 characters")]
        public string CustomerName { get; set; } = "Cool Solutions";

        [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Customer email is required")]
        [System.ComponentModel.DataAnnotations.EmailAddress(ErrorMessage = "Please enter a valid email address")]
        public string CustomerEmail { get; set; } = "contact@coolsolutions.com";

        public string CustomerPhone { get; set; } = "+1 (555) 987-6543";

        [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Customer address is required")]
        public string CustomerAddress { get; set; } = "456 Industrial Ave\nSuite 789\nLos Angeles, CA 90001";
        public string ItemDescription { get; set; } = string.Empty;

        private int _quantity = 1;
        public int Quantity
        {
            get => _quantity;
            set
            {
                _quantity = value;
                UpdateTotalAmount();
            }
        }

        private decimal _unitPrice = 0;
        public decimal UnitPrice
        {
            get => _unitPrice;
            set
            {
                _unitPrice = value;
                UpdateTotalAmount();
            }
        }

        public decimal TotalAmount => Quantity * UnitPrice;
        public string TotalAmountDisplay { get; set; } = "$0.00";

        public void SetUnitPrice(decimal price)
        {
            _unitPrice = price;
        }

        public void SetQuantity(int quantity)
        {
            _quantity = quantity;
            UpdateTotalAmount();
        }
        private void UpdateTotalAmount()
        {
            try
            {
                var total = TotalAmount;
                TotalAmountDisplay = $"${total:N2}";
            }
            catch
            {
                TotalAmountDisplay = "$0.00";
            }
        }
        
        public string Notes { get; set; } = string.Empty;
    }
}

<style>
    .invoice-form-card {
        background: white;
        border-radius: 12px;
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
        padding: 2rem;
        margin-bottom: 2rem;
    }

    .invoice-form {
        max-width: 100%;
    }

    .form-section {
        margin-bottom: 2rem;
        padding: 1.5rem;
        border: 1px solid #e9ecef;
        border-radius: 8px;
        background-color: #fdfdfd;
    }

    .section-title {
        font-weight: 600;
        color: #495057;
        font-size: 1.1rem;
        margin-bottom: 1rem;
        border-bottom: 2px solid #007acc;
        padding-bottom: 0.5rem;
    }

    .form-label {
        font-weight: 500;
        color: #495057;
        margin-bottom: 0.5rem;
    }

    .readonly-field {
        background-color: #f8f9fa !important;
        border-color: #e9ecef !important;
        color: #6c757d;
    }

    .editable-field {
        border-color: #007acc;
    }

        .editable-field:focus {
            border-color: #0056b3;
            box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
        }

    .total-field {
        font-weight: bold;
        font-size: 1.1em;
        background-color: #e7f3ff !important;
    }

    .generate-btn {
        min-width: 200px;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 0.5rem;
    }

    .pdf-placeholder {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        border-radius: 12px;
        min-height: 600px;
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
    }

    .pdf-content {
        text-align: center;
        padding: 2rem;
    }

        .pdf-content h3 {
            font-weight: 300;
            margin-bottom: 0.5rem;
        }

        .pdf-content p {
            opacity: 0.8;
            font-size: 1.1rem;
        }

    .loader-overlay {
        background: linear-gradient(135deg, #007acc 0%, #0056b3 100%);
        border-radius: 12px;
        min-height: 600px;
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
        animation: pulse-bg 2s ease-in-out infinite alternate;
    }

    .loader-content {
        text-align: center;
        padding: 2rem;
    }

        .loader-content h3 {
            font-weight: 400;
            margin-bottom: 0.5rem;
            font-size: 1.5rem;
        }

        .loader-content p {
            opacity: 0.9;
            font-size: 1.1rem;
            margin-bottom: 0;
        }

    @@keyframes pulse-bg {
        0% {
            background: linear-gradient(135deg, #007acc 0%, #0056b3 100%);
        }

        100% {
            background: linear-gradient(135deg, #0056b3 0%, #004085 100%);
        }
    }
    
    @@media (max-width: 768px) {
        .invoice-form-card {
            padding: 1rem;
            margin: 0.5rem;
        }

        .pdf-placeholder,
        .loader-overlay {
            min-height: 400px;
            margin: 0.5rem;
        }

        .form-section {
            padding: 1rem;
        }

        .loader-content h3 {
            font-size: 1.3rem;
        }

        .loader-content p {
            font-size: 1rem;
        }
    }
</style>

Dans le code ci-dessus, vous pouvez remarquer que j’ai utilisé plusieurs composants Telerik pour créer le formulaire client, tel que TelerikSvgIcon, TelerikTextBox, TelerikTextArea, TelerikDatePickerentre autres, car leur flexibilité permet la création rapide d’interfaces graphiques et même l’ajout de validations si nécessaire.

Le résultat est une belle interface graphique qui nous prépare à l’implémentation du composant PDFViewer:

Maintenant, voyons comment implémenter le contrôle PDFViewer dans notre projet.

Affichage d’un PDF avec le composant PDFViewer

Pour ajouter le composant PDFViewer dans un projet Blazor, vous devez ajouter le TelerikPdfViewer Tag comme dans l’exemple suivant:

@if (showPdfPlaceholder)
{
    <div class="row">
        <div class="col-12">
            <div class="pdf-placeholder">
                <div class="pdf-content">

                    <TelerikPdfViewer />

                </div>
            </div>
        </div>
    </div>
}

Cela affichera le composant sur l’interface utilisateur, permettant des actions telles que l’ouverture d’un document PDF:

Dans notre cas, n’oubliez pas que nous voulons charger le document PDF généré simulé, nous utiliserons donc le Data paramètre, de type byte[]qui peut être utilisé pour charger le contenu du document dont nous avons besoin. Pour notre exemple, j’ai ajouté un document PDF statique dans wwwrootpour le charger dans le composant comme suit:

...
    @if (showPdfPlaceholder)
    {
        <div class="row">
            <div class="col-12">
                <div class="pdf-placeholder">
                    <div class="pdf-content">

                        <TelerikPdfViewer Data="@PdfSource"/>

                    </div>
                </div>
            </div>
        </div>
    }
...

@code {   
    private byte[] PdfSource { get; set; }
    
    ...    
    
    private async Task OnValidSubmit()
    {
       ...

        
        var url = NavigationManager.ToAbsoluteUri("invoice.pdf");        
        var pdfBytes = await Http.GetByteArrayAsync(url);
        
        PdfSource = pdfBytes;

        showPdfPlaceholder = true;
        StateHasChanged();
    }

...

Dans le code ci-dessus, nous chargeons les octets du document en utilisant le GetByteArrayAsync Méthode, attribuant son contenu à la définition précédemment définie PdfSource propriété. Puisque nous avons attribué cette propriété à la Data Paramètre du composant PDFViewer, il est automatiquement ouvert:

Personnalisation de la barre d’outils PDFViewer

Une chose que vous voudrez probablement faire en fonction du scénario particulier dans lequel vous implémentez PDFViewer est de personnaliser les outils que vous souhaitez que vos utilisateurs utilisent. Par exemple, si votre application doit afficher les factures générées pour vos clients, il est inutile d’afficher l’outil pour ouvrir d’autres documents PDF.

Vous pouvez voir une liste de tous les Outils pris en charge par PDFViewer dans la documentation officielle. Pour notre exemple pratique, nous ne laisserons que le Télécharger et Imprimer Outils, ce qui est très facile à faire en définissant les balises correspondantes:

...
    <TelerikPdfViewer Data="@PdfSource">

        <PdfViewerToolBar>
            <PdfViewerToolBarDownloadTool />
            <PdfViewerToolBarPrintTool />
        </PdfViewerToolBar>

    </TelerikPdfViewer>
...

De même, si vous ne trouvez pas l’outil dont vous avez besoin pour votre PDF, vous pouvez le créer via le PdfViewerToolBarCustomTool Tag, où vous pouvez ajouter le code avec lequel l’utilisateur interagira. Par exemple, supposons que nous voulons que l’utilisateur puisse traduire ses factures en espagnol simplement en cliquant sur un bouton:

...
    @if (showPdfPlaceholder)
    {
        <div class="row">
            <div class="col-12">
                <div class="pdf-placeholder">
                    <div class="pdf-content">                    
                        <TelerikPdfViewer Data="@PdfSource">
                            <PdfViewerToolBar>
                                <PdfViewerToolBarDownloadTool />
                                <PdfViewerToolBarPrintTool />

                                <PdfViewerToolBarCustomTool>
                                    <TelerikButton OnClick="@TranslateInvoice">Translate Invoice
                                    </TelerikButton>
                                </PdfViewerToolBarCustomTool>

                             </PdfViewerToolBar>
                        </TelerikPdfViewer>
                    </div>
                </div>
            </div>
        </div>
    }
...

@code {

...

    private async Task TranslateInvoice()
    {
        showPdfPlaceholder = false;
        isGenerating = true;
        StateHasChanged();
        await Task.Delay(3000);
        isGenerating = false;

        var translatedFile = NavigationManager.ToAbsoluteUri("Invoice_es.pdf");
        var bytes = await Http.GetByteArrayAsync(translatedFile);
        PdfSource = bytes;
        showPdfPlaceholder = true;
    }
...

Cela nous permettra d’ajouter les fonctionnalités dont nous avons besoin pour nos scénarios:

Événements disponibles dans le PDFViewer

Le contrôle PDFViewer a les quatre événements suivants:

  • OnDownload: Tiré lorsque l’utilisateur clique sur le bouton de téléchargement.
  • OnError: Tiré lorsqu’une erreur se produit lors de la tentative d’ouvrir un fichier, comme lors de la tentative de chargement un fichier corrompu ou un format incorrect.
  • OnOpen: Firé lorsque l’utilisateur ouvre un fichier à partir de la barre d’outils.
  • ZoomChanged: Tiré lorsque l’utilisateur clique sur les boutons de zoom dans / out.

Chacun de ces événements reçoit différents types d’arguments, que vous pouvez voir ci-dessous:

...
    @if (showPdfPlaceholder)
    {
        <div class="row">
            <div class="col-12">
                <div class="pdf-placeholder">
                    <div class="pdf-content">                    
                        <TelerikPdfViewer Data="@PdfSource"
                                OnDownload="OnDownload"
                                OnError="OnError"
                                OnOpen="OnOpen"
                                ZoomChanged="ZoomChanged">
                           ...
                        </TelerikPdfViewer>
                    </div>
                </div>
            </div>
        </div>
    }
...

@code {

...
    
    private void OnDownload(PdfViewerDownloadEventArgs args)
    {        
        args.FileName = $"Invoice_{DateTime.Now:yyyyMMdd_HHmmss}.pdf";
    }
    private void OnError(PdfViewerErrorEventArgs args)
    {
        Debug.WriteLine($"PDF Viewer Error: {args.Message}");
    }
    private void OnOpen(PdfViewerOpenEventArgs args)
    {
        Debug.WriteLine($"PDF Viewer Opened: {args.Files.FirstOrDefault()}");
       
    }
    private void ZoomChanged(decimal args)
    {
        Debug.WriteLine($"Zoom changed to: {args}");
    }

...

}

...

Sans aucun doute, il existe des scénarios où ces événements sont extrêmement utiles, comme dans le code précédent, où le fichier téléchargé est renommé pendant le OnDownload événement suivant un modèle défini par nous.

Autres paramètres utiles du contrôle PDFViewer

Le contrôle PDFViewer a d’autres paramètres que nous pouvons configurer pour le personnaliser davantage. Certains d’entre eux sont:

Class: Permet de personnaliser le composant et de remplacer le thème
Height et Width: Autoriser le réglage de la hauteur et de la largeur du contrôle
MaxZoom / / MinZoom: Autoriser l’attribution d’un niveau de zoom maximum et minimum sur le composant
Zoom / / ZoomRate: Autoriser l’obtention et la définition du niveau de zoom actuel et du zoom

Conclusion

Tout au long de cet article, vous avez vu de manière pratique comment mettre en œuvre le contrôle du télérik pdfViewer dans vos projets basés sur un blazor. Vous avez également appris à personnaliser la barre d’outils et à charger des documents PDF. De plus, vous avez appris les événements disponibles dans le composant et d’autres paramètres utiles. Il est temps de remplacer ces liens de téléchargement vers vos PDF par un composant PDFViewer et d’afficher les documents directement dans vos applications.

N’oubliez pas, Telerik UI pour Blazor est livré avec un essai gratuit de 30 jours:

Essayer maintenant




Source link
Quitter la version mobile