Example T178798
Visible to All Users

Reporting for Web Forms - Report Designer with Report Storage and Custom Command

This example integrates the End-User Report Designer into an ASP.NET WebForms application and implements a Microsoft SQL Server database report storage to add, edit and delete reports. This example also demonstrates how to add custom commands to the report designer menu at runtime. A custom Close menu command redirects the user to the homepage.

Report Designer with Report Storage and Custom Command

Before you start

Create Reports Database

Create a Reports database in the local Microsoft SQL Server. Add the ReportLayout table with the following script:

SQL

SQL
USE [Reports] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[ReportLayout]( [ReportId] [int] IDENTITY(1,1) NOT NULL, [DisplayName] [nvarchar] NULL, [LayoutData] [varbinary] NULL, [ReportId] ASC CONSTRAINT [PK_ReportLayout6] PRIMARY KEY CLUSTERED ( )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO

SQL Server 2016 - v13.0.x.x

SQL
USE [Reports] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[ReportLayout]( [ReportId] [int] IDENTITY(1,1) NOT NULL , [DisplayName] [nvarchar](50) NULL, [LayoutData] [varbinary](max) NULL, CONSTRAINT PK_ReportLayout PRIMARY KEY (ReportId) ); SELECT * FROM [dbo].[ReportLayout] ORDER BY [ReportId] ASC GO

Create Northwind Database

Create the Northwind database and add it to your local Microsoft SQL server.

Files to Review

Documentation

More Examples

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

SimpleWebReportCatalog/App_Code/CustomReportStorageWebExtension.cs(vb)
C#
using DevExpress.XtraReports.UI; using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.IO; using System.Linq; namespace SimpleWebReportCatalog { public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension { private DataTable reportsTable = new DataTable(); private SqlDataAdapter reportsTableAdapter; public CustomReportStorageWebExtension() { string connectionString = ConfigurationManager.ConnectionStrings["catalogConnectionString"].ConnectionString; reportsTableAdapter = new SqlDataAdapter("Select * from ReportLayout", new SqlConnection(connectionString)); SqlCommandBuilder builder = new SqlCommandBuilder(reportsTableAdapter); reportsTableAdapter.InsertCommand = builder.GetInsertCommand(); reportsTableAdapter.UpdateCommand = builder.GetUpdateCommand(); reportsTableAdapter.DeleteCommand = builder.GetDeleteCommand(); reportsTableAdapter.Fill(reportsTable); DataColumn[] keyColumns = new DataColumn[1]; keyColumns[0] = reportsTable.Columns[0]; reportsTable.PrimaryKey = keyColumns; } public override bool CanSetData(string url) { return GetUrls()[url].Contains("ReadOnly") ? false : true; } public override byte[] GetData(string url) { // Get the report data from the storage. DataRow row = reportsTable.Rows.Find(int.Parse(url)); if (row == null) return null; byte[] reportData = (Byte[])row["LayoutData"]; return reportData; } public override Dictionary<string, string> GetUrls() { reportsTable.Clear(); reportsTableAdapter.Fill(reportsTable); // Get URLs and display names for all reports available in the storage. var v = reportsTable.AsEnumerable() .ToDictionary<DataRow, string, string>(dataRow => ((Int32)dataRow["ReportId"]).ToString(), dataRow => (string)dataRow["DisplayName"]); return v; } public override bool IsValidUrl(string url) { return true; } public override void SetData(XtraReport report, string url) { // Write a report to the storage under the specified URL. DataRow row = reportsTable.Rows.Find(int.Parse(url)); if (row != null) { using (MemoryStream ms = new MemoryStream()) { report.SaveLayoutToXml(ms); row["LayoutData"] = ms.GetBuffer(); } reportsTableAdapter.Update(reportsTable); } } public override string SetNewData(XtraReport report, string defaultUrl) { // Append "1" if a new report name already exists. if (GetUrls().ContainsValue(defaultUrl)) defaultUrl = string.Concat(defaultUrl,"1"); // Save a report to the storage with a new URL. // The defaultUrl parameter is the report name that the user specifies. DataRow row = reportsTable.NewRow(); row["ReportId"] = 0; row["DisplayName"] = defaultUrl; using (MemoryStream ms = new MemoryStream()) { report.SaveLayoutToXml(ms); row["LayoutData"] = ms.GetBuffer(); } reportsTable.Rows.Add(row); reportsTableAdapter.Update(reportsTable); // Refill the dataset to obtain the actual value of the new row's autoincrement key field. reportsTable.Clear(); reportsTableAdapter.Fill(reportsTable); return reportsTable.AsEnumerable(). FirstOrDefault(x => x["DisplayName"].ToString() == defaultUrl)["ReportId"].ToString(); } } }
SimpleWebReportCatalog/Default.aspx
ASPx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SimpleWebReportCatalog.Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="CatalogForm" runat="server"> <div> <h1>Report Catalog </h1> <p>Use the form below to manage reports in the catalog.</p> </div> <div> <asp:ListBox ID="reportsList" runat="server" Width="600px" Height="150px"></asp:ListBox> </div> <div> <asp:Button CssClass="catalogButton" ID="editButton" runat="server" Text="Edit" OnClick="EditButton_Click" /> <asp:Button CssClass="catalogButton" ID="deleteButton" runat="server" Text="Delete" OnClick="DeleteButton_Click" /> </div> <hr /> <div> <asp:Button CssClass="catalogButton" ID="newReportButton" runat="server" Text="New Report" OnClick="NewReportButton_Click" /> </div> </form> </body> </html>
SimpleWebReportCatalog/Default.aspx.cs(vb)
C#
using System; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.Web.UI.WebControls; // ... namespace SimpleWebReportCatalog { public partial class Default : System.Web.UI.Page { private DataTable reportsTable = new DataTable(); private SqlDataAdapter reportsTableAdapter; protected void Page_Load(object sender, EventArgs e) { string connectionString = ConfigurationManager.ConnectionStrings["catalogConnectionString"].ConnectionString; reportsTableAdapter = new SqlDataAdapter("Select * from ReportLayout", new SqlConnection(connectionString)); SqlCommandBuilder builder = new SqlCommandBuilder(reportsTableAdapter); reportsTableAdapter.InsertCommand = builder.GetInsertCommand(); reportsTableAdapter.UpdateCommand = builder.GetUpdateCommand(); reportsTableAdapter.DeleteCommand = builder.GetDeleteCommand(); reportsTableAdapter.Fill(reportsTable); DataColumn[] keyColumns = new DataColumn[1]; keyColumns[0] = reportsTable.Columns[0]; reportsTable.PrimaryKey = keyColumns; if (!IsPostBack) { reportsList.DataSource = reportsTable; reportsList.DataMember = "Reports"; reportsList.DataTextField = "DisplayName"; reportsList.DataValueField = "ReportId"; this.DataBind(); } } protected void NewReportButton_Click(object sender, EventArgs e) { Session["DesignerTask"] = new DesignerTask { mode = ReportEdditingMode.NewReport, }; Response.Redirect("Designer.aspx"); } protected void EditButton_Click(object sender, EventArgs e) { ListItem selected = reportsList.SelectedItem; if(selected != null) { Session["DesignerTask"] = new DesignerTask { mode = ReportEdditingMode.ModifyReport, reportID = selected.Value }; Session["ReportID"] = selected.Value; Response.Redirect("Designer.aspx"); } } protected void DeleteButton_Click(object sender, EventArgs e) { ListItem selected = reportsList.SelectedItem; if (selected != null) { DataRow row = reportsTable.Rows.Find(int.Parse(selected.Value)); if (row != null) { row.Delete(); reportsTableAdapter.Update(reportsTable); reportsTable.AcceptChanges(); } reportsList.Items.Remove(reportsList.SelectedItem); } } } }
SimpleWebReportCatalog/Designer.aspx
ASPx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Designer.aspx.cs" Inherits="SimpleWebReportCatalog.Designer" %> <%@ Register Assembly="DevExpress.XtraReports.v24.2.Web.WebForms, Version=24.2.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.XtraReports.Web" TagPrefix="dx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <link rel="stylesheet" href="Styles/DesignerStyle.css" /> <script type="text/javascript"> // The CustomizeMenuActions event handler. function CustomizeMenuActions(s, e) { var actions = e.Actions; // Register the custom Close menu command. actions.push({ text: "Close", imageClassName: "customButton", disabled: ko.observable(false), visible: true, // The clickAction function recieves the client-side report model // and allows you to interact with the opened report. clickAction: function (report) { window.location = "Default.aspx"; }, container: "menu" }); } </script> </head> <body> <form id="form1" runat="server"> <div> <dx:ASPxReportDesigner ID="ASPxReportDesigner1" runat="server" ClientSideEvents-CustomizeMenuActions="CustomizeMenuActions"> </dx:ASPxReportDesigner> </div> </form> </body> </html>
SimpleWebReportCatalog/Designer.aspx.cs(vb)
C#
using System; using DevExpress.DataAccess.Sql; namespace SimpleWebReportCatalog { public partial class Designer : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { DesignerTask task = (DesignerTask)Session["DesignerTask"]; if(task != null) { InitDesignerPage(task); } else if(!Page.IsCallback) { Response.Redirect("Default.aspx"); } } private void InitDesignerPage(DesignerTask task) { BindToData(); switch(task.mode) { case ReportEdditingMode.NewReport: // Create a new report from the template. ASPxReportDesigner1.OpenReport(new ReportTemplate()); break; case ReportEdditingMode.ModifyReport: // Load a report from the report storage. ASPxReportDesigner1.OpenReport(task.reportID); break; } } private void BindToData() { SqlDataSource ds = new SqlDataSource("Northwind"); CustomSqlQuery query = new CustomSqlQuery(); query.Name = "Products"; query.Sql = "SELECT * FROM Products"; ds.Queries.Add(query); ds.RebuildResultSchema(); ASPxReportDesigner1.DataSources.Add("Northwind", ds); } } }
SimpleWebReportCatalog/DesignerTask.cs(vb)
C#
namespace SimpleWebReportCatalog { public enum ReportEdditingMode { NewReport, ModifyReport }; public class DesignerTask { public ReportEdditingMode mode { get; set; } public string reportID { get; set; } } }
SimpleWebReportCatalog/Global.asax.cs(vb)
C#
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Security; using System.Web.SessionState; namespace SimpleWebReportCatalog { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.EnableCustomSql(); DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension.RegisterExtensionGlobal(new CustomReportStorageWebExtension()); DevExpress.XtraReports.Web.ASPxReportDesigner.StaticInitialize(); } protected void Session_Start(object sender, EventArgs e) { } protected void Application_BeginRequest(object sender, EventArgs e) { } protected void Application_AuthenticateRequest(object sender, EventArgs e) { } protected void Application_Error(object sender, EventArgs e) { } protected void Session_End(object sender, EventArgs e) { } protected void Application_End(object sender, EventArgs e) { } } }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.