You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DNN模块中GemBox生成导出文件后的代码中断及模块刷新问题咨询

Alright, let's break down your problem and walk through practical solutions step by step. I've dealt with similar DNN + file download scenarios before, so here's what I recommend:

1. Why Your RegisterStartupScript Isn't Working

The core issue is context: your standalone .aspx file sends a file download response (Excel/PDF/CSV) to the browser. When the browser receives this, it focuses entirely on saving the file—it won’t parse or execute any HTML/JavaScript embedded in that response. Even if your script was output correctly, the browser treats the response as a binary file, not a web page. Plus, DNN’s module scope is tied to the parent page (your .ascx), so the standalone page can’t access the parent’s JavaScript environment anyway.

2. Fixing the Loading Spinner + Module Reload

You have two solid options, depending on how you want to structure your code:

Option A: AJAX + Blob (Cleaner, Full Control)

This approach keeps everything in the parent page’s context, so you can easily manage the spinner and module reload without relying on separate pages:

Frontend (jQuery)

$("#exportButton").click(function() {
    // Show loading spinner immediately
    $("#loadingSpinner").show();

    // Get user's selected export format
    var exportFormat = $("#formatDropdown").val();

    // AJAX call to generate the file
    $.ajax({
        url: "/DesktopModules/YourModule/ExportHandler.ashx",
        type: "POST",
        data: { format: exportFormat },
        xhrFields: { responseType: "blob" }, // Critical: tells jQuery to expect binary data
        success: function(blob) {
            // Create a temporary download link
            var url = window.URL.createObjectURL(blob);
            var downloadLink = $("<a>")
                .attr("href", url)
                .attr("download", `PriceList.${exportFormat.toLowerCase()}`)
                .appendTo("body");
            
            // Trigger the download
            downloadLink[0].click();
            
            // Clean up
            window.URL.revokeObjectURL(url);
            downloadLink.remove();
        },
        error: function(xhr) {
            alert(`Export failed: ${xhr.responseText}`);
        },
        complete: function() {
            // Hide spinner AND reload the DNN module
            $("#loadingSpinner").hide();
            reloadModule();
        }
    });
});

// Reload DNN module without full page refresh
function reloadModule() {
    var moduleId = <%= ModuleId %>; // Pull module ID from code-behind
    
    // Option 1: Use DNN's built-in module reload API (preferred)
    if (typeof dnn !== "undefined" && dnn.modules) {
        dnn.modules.reload(moduleId);
    } 
    // Option 2: Manual AJAX fallback if DNN API isn't available
    else {
        $.get(`/DesktopModules/YourModule/View.ascx?moduleId=${moduleId}`, function(html) {
            // Replace module content (adjust selector to match your module's container)
            $(`#dnn_ctr${moduleId}_Content`).html(html);
        });
    }
}

Backend (ASHX Handler)

Create an ExportHandler.ashx file to handle file generation without interrupting the parent page:

public class ExportHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string format = context.Request.Form["format"];
        
        // 1. Fetch price list from your ERP system
        var priceList = GetPriceListFromERP();
        
        // 2. Generate file with GemBox
        using var stream = new MemoryStream();
        switch (format.ToUpper())
        {
            case "EXCEL":
                var workbook = new ExcelFile();
                // Populate workbook with priceList data...
                workbook.Save(stream, SaveOptions.XlsxDefault);
                context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                context.Response.AddHeader("Content-Disposition", "attachment; filename=PriceList.xlsx");
                break;
            case "CSV":
                // Generate CSV (use GemBox or direct string writing)
                // ...
                context.Response.ContentType = "text/csv";
                context.Response.AddHeader("Content-Disposition", "attachment; filename=PriceList.csv");
                break;
            case "PDF":
                var document = new PdfDocument();
                // Populate PDF with priceList data...
                document.Save(stream);
                context.Response.ContentType = "application/pdf";
                context.Response.AddHeader("Content-Disposition", "attachment; filename=PriceList.pdf");
                break;
        }
        
        // Send the file to the client
        stream.Position = 0;
        stream.CopyTo(context.Response.OutputStream);
        // No Response.End() needed here—we're just writing the stream, no thread abort
    }

    private List<PriceListItem> GetPriceListFromERP()
    {
        // Your ERP integration logic here
        return new List<PriceListItem>();
    }

    public bool IsReusable => false;
}

Option B: Hidden iFrame (Simpler, Uses Your Existing .aspx)

If you want to keep using your standalone .aspx file, use a hidden iframe to trigger the download, then listen for when it finishes loading to clean up:

Frontend

$("#exportButton").click(function() {
    $("#loadingSpinner").show();
    var exportFormat = $("#formatDropdown").val();
    
    // Create hidden iframe to trigger download
    var iframe = $("<iframe>")
        .attr("src", `/DesktopModules/YourModule/ExportPage.aspx?format=${exportFormat}`)
        .css("display", "none")
        .appendTo("body");
    
    // Listen for iframe load (fires when download completes)
    iframe.on("load", function() {
        $("#loadingSpinner").hide();
        reloadModule();
        // Clean up the iframe
        $(this).remove();
    });
});

// Same reloadModule function as Option A
function reloadModule() {
    var moduleId = <%= ModuleId %>;
    if (dnn && dnn.modules) {
        dnn.modules.reload(moduleId);
    } else {
        $.get(`/DesktopModules/YourModule/View.ascx?moduleId=${moduleId}`, function(html) {
            $(`#dnn_ctr${moduleId}_Content`).html(html);
        });
    }
}

3. Why the Original PostBack Broke

ASP.NET’s Response.End() (often called implicitly during file downloads) triggers a ThreadAbortException that cuts off the rest of the PostBack lifecycle. Even with GemBox’s workaround, DNN’s partial rendering logic gets confused because the response is hijacked for the file download. Using a separate handler/iframe avoids this entirely, as it doesn’t interfere with the parent page’s PostBack flow.

Final Notes

  • Web Workers: You don’t need them here—either AJAX/Blob or iframe will handle the scenario without blocking the UI.
  • DNN Module Context: When reloading, make sure your View.ascx can handle the GET request with the moduleId parameter and returns the correct module HTML.
  • Cross-Browser Testing: Verify behavior across Chrome, Firefox, and Edge to ensure consistent download and reload functionality.

内容的提问来源于stack exchange,提问作者Tig7r

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:37:39