/* i-net software provides programming examples for illustration only, without warranty either expressed or implied, including, but not limited to, the implied warranties of merchantability and/or fitness for a particular purpose. This programming example assumes that you are familiar with the programming language being demonstrated and the tools used to create and debug procedures. i-net software support professionals can help explain the functionality of a particular procedure, but they will not modify these examples to provide added functionality or construct procedures to meet your specific needs. © i-net software 1998-2013 */ using System; using System.Collections.Generic; using System.Windows.Forms; using Inet.Viewer.Data; using Inet.Viewer.Resources; using Inet.Viewer.WinForms.Export; using System.Drawing; using System.Drawing.Printing; using Inet.Viewer.WinForms.Prompt; namespace Inet.Viewer.WinForms { /// /// The class Report View contains all information needed to show one report. ///
/// To use this class correctly you need to set the Property ReportData with an implementation of the interface /// IReportData /// ReportView uses the Property ReportData as the data source for the rendering ///
public partial class ReportView : UserControl, IReportView { private const int PingInterval = 300000; /// /// Used with the Property PageCount /// private int pageCount = 0; /// /// For the Property IsClosable /// private bool isCloseable; /// /// For the Property HasPrompts /// private bool hasPrompts; /// /// For the Property IsPageLimit Exceeded /// private bool isPageLimitExceeded; /// /// For the Property ReportTitle /// private string reportTitle; /// /// For the Property ViewMode /// private PageViewMode viewMode; /// /// For the Property MouseActionMode /// private MouseMode mouseActionMode; /// /// For the Property ReportViewer /// private ReportViewer reportViewer; /// /// /// private string customReportTitle; /// /// /// private bool hasCustomReportTitle; /// /// Used with the Property ReportRenderData /// private ReportDataCache rptDataCache; /// /// /// private ReportInfo reportInfo; /// /// An optional parent in the GUI. /// private TabPage tabPage; /// /// the last timestamp of the data /// private long lastTimestamp; /// /// /// private ReportContentView contentView; private MyMessageBox box; /// /// /// public event PageChanged PageChanged; /// /// /// public event EventHandler ZoomChanged; /// /// Occured when data was initially received. /// public event DataChanged DataInited; /// /// event that is thrown when there are changes in the data. E.g. if it was refreshed. /// public event DataChanged DataChanged; private Timer pingTimer = new Timer(); private LoadProgress progress; private object progressMutex = new object(); private bool tabLabelDefined; PrintDialog printDialog = new PrintDialog(); private PromptDialogForm promptDialog; private bool origPromptOnRefresh; /// /// Constructor to initialize with a valid ReportRenderData object /// Sets the system locale as Parameter to the url /// Throws NullReferenceException if data is null /// /// Invokes the method ParseUrlParameters() to read out parameters from locationUrl(ReportRenderData) /// public ReportView() { InitializeComponent(); this.IsCloseable = true; this.pageCount = -1; // min and max have to be set before the ZoomFactor this.MouseActionMode = MouseMode.Panning; CreateViewPanel(); contentView = CreateContentView(); contentView.ZoomChanged += (sender, args) => { if (ZoomChanged != null) { ZoomChanged(this, args); } }; contentView.CurrentPage = 1; contentView.ReportView = this; contentView.PageChanged += (sender, pageNum) => { if (PageChanged != null) { PageChanged(this, pageNum); } }; Disposed += ReportView_Disposed; this.splitContainer1.Panel2.Controls.Add((Control)contentView); } /// /// Called when the view is disposed. /// /// the sender /// the arguments void ReportView_Disposed(object sender, EventArgs e) { contentView.Dispose(); } /// /// Creates the content view instance. /// /// the content view instance virtual internal ReportContentView CreateContentView() { return new ReportContentView(); } /// /// Set a parent TabPage. The Title will be changed if the data are available /// internal TabPage TabPage { set { tabPage = value; } } /// /// /// public PageViewMode ViewMode { get { return viewMode; } set { viewMode = value; } } /// /// /// public MouseMode MouseActionMode { get { return mouseActionMode; } set { mouseActionMode = value; } } /// /// The filename of the Report /// /// public string FileName { get { if (this.ReportInfo != null) { return ReportInfo.FileName; } else { return null; } } } /// /// The currently shown page. /// When this property is set the report will switch automatically to that page. But only if /// the page is in the range between 1 and the . Otherwise the CurrentPage will not be changed /// /// By default the CurrentPage is 1 /// public int CurrentPage { get { return contentView.CurrentPage; } set { contentView.CurrentPage = value; } } /// /// Clears any hightlighted texts. /// public void ClearSelection() { contentView.ClearSelection(); } /// /// Highlights the texts described by the specified search chunks. /// Scrolls to the first highlighted text. If the first hightlighted /// text is on a different page than currently shown, this view switches /// to that page. /// /// the texts to hightlights public void Highlight(SearchChunk[] searchChunks) { contentView.HighlightedSearchChunks = searchChunks; } /// /// Sets the input focus to the content control. /// public void FocusContent() { contentView.Focus(); } /// /// Gets the page info instance of the last rendered page. /// public PageInfo PageInfo { get { return contentView.PageInfo; } } /// /// The ReportRenderData holds the implementation of the IReportRenderData /// Throws NullReferenceException if data is null /// public IRenderData ReportData { get { return rptDataCache.ReportData; } set { if (value == null) { throw new NullReferenceException(strings.ErrorMessage_ReportView_SetDataNotNull); } this.rptDataCache = new ReportDataCache(value); this.reportTitle = value.ReportTitle; contentView.ReportDataCache = rptDataCache; origPromptOnRefresh = value.PromptOnRefresh; if (IsHandleCreated) { StartBackgroundThreads(); } } } /// /// Starts the background threads for loading the page count, group tree, page limit and initial page data. /// private void StartBackgroundThreads() { SetProgressStatus(Progress.ProgressStatus.Error); // Create the ReportPageView contentView.Focus(); contentView.InitialPageLoad(); ThreadManager.RequestPageCount(this, rptDataCache, SetPageCount); ThreadManager.RequestPageLimitExceed(this, rptDataCache, SetPageLimitExceeded); this.groupTreeView.SetupTreeView(); StartProgress(); pingTimer.Tick += new EventHandler((e, a) => { if (progress == null) { ThreadManager.RequestPing(ReportData); } }); pingTimer.Interval = PingInterval; pingTimer.Start(); } /// /// Starts the progress for loading the report. The progress only retrieves /// the render states from the server and doesn't perform the actual loading. /// private void StartProgress() { LoadProgress newProgress = new LoadProgress(this.ShowError, ReportData); LoadProgress oldProgress; lock (progressMutex) { oldProgress = this.progress; this.progress = newProgress; } if (oldProgress != null) { oldProgress.Status = Progress.ProgressStatus.Error; } if (reportViewer == null) { return; } reportViewer.ToolBar.AddProgress(newProgress); newProgress.StartProgress(); contentView.LoadProgress = progress; } /// /// Gets the report data chache. /// internal ReportDataCache ReportDataCache { get { return rptDataCache; } } /// /// Defines if this ReportView can be closed in the TabView or not /// /// Default is true public bool IsCloseable { get { return isCloseable; } set { isCloseable = value; } } /// /// If the report has Prompts they need to be shown before the acutal report can be rendered /// public bool HasPrompts { get { return hasPrompts; } set { hasPrompts = value; } } /// /// The title of the report. Typically displayed in a title bar, e.g. "Employee Report 2005". /// By default the title is received from the server. /// If the title is set manually than this text is used for the title. /// public string ReportTitle { get { if (hasCustomReportTitle) { return customReportTitle; } else { return reportTitle; } } set { customReportTitle = value; hasCustomReportTitle = true; } } /// /// The amount of pages for this report /// public int PageCount { get { return pageCount; } } /// /// Callback for the ThreadManager /// /// /// private void SetPageCount(int pageCount, Exception ex) { if (ex != null) { ShowError(ex); } else { if (this.pageCount != pageCount) { this.pageCount = pageCount; contentView.PageCount = pageCount; this.OnPageChanged(); } LoadProgress progress; lock (progressMutex) { progress = this.progress; this.progress = null; } if (progress != null) { // when page count arrives the loading progress is completed progress.Status = Progress.ProgressStatus.Completed; progress = null; } } ThreadFinished(); } /// /// Callback for the ThreadManager /// /// /// private void SetPageLimitExceeded(bool exceeded, Exception ex) { if (ex != null) { ShowError(ex); } else { PageLimitExceeded = exceeded; } ThreadFinished(); } /// /// Creates a new instance of the ReportViewPanel for this IReportView. Including the ReportPageView /// private void CreateViewPanel() { Name = Inet.Viewer.WinForms.ReportViewer.ReportViewPanelName; Dock = DockStyle.Fill; AutoSize = false; AutoScroll = true; this.groupTreeView.ReportView = this; } /// /// /// protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); if (rptDataCache != null) { StartBackgroundThreads(); } } /// /// Fires the PageChanged Event /// private void OnPageChanged() { if (PageChanged != null) { PageChanged(this, CurrentPage); } } /// /// Check if the timestamp of the data has changed. Throw an event the data has changed. /// /// internal void CheckDataTimestamp(long timestamp) { if (lastTimestamp != timestamp) { bool first = lastTimestamp == 0; lastTimestamp = timestamp; if (!first) { rptDataCache.Clear(); if (DataChanged != null) { DataChanged(this, timestamp); } ThreadManager.RequestPageCount(this, rptDataCache, SetPageCount); ThreadManager.RequestPageLimitExceed(this, rptDataCache, SetPageLimitExceeded); groupTreeView.SetupTreeView(); Invoke((MethodInvoker)delegate { contentView.ClearSelection(); }); } else if (DataInited != null) { DataInited(this, timestamp); } } } /// /// Navigates to the first page of the report /// public void FirstPage() { CurrentPage = 1; } /// /// Navigates to the last page of the report /// public void LastPage() { CurrentPage = PageCount; } /// /// Navigates to the next page of the report /// public void NextPage() { if (contentView.IsDoublePageMode() && !contentView.IsHScroll() && (CurrentPage + 1 < pageCount || pageCount == -1)) { CurrentPage += 2; } else if (CurrentPage < PageCount || pageCount == -1) { CurrentPage++; } } /// /// Navigates to the previous page of the report /// public void PreviousPage() { if (contentView.IsDoublePageMode() && !contentView.IsHScroll() && CurrentPage > 2) { CurrentPage -= 2; } else if (CurrentPage > 1) { CurrentPage--; } } /// /// Callback for the prompt dialog. /// /// prompt data /// private void EnteredPrompts(List prompts, bool promptRefresh) { foreach (PromptData prompt in prompts) { if (prompt == null) { continue; } if (prompt.Values == null) { this.ReportData[("prompt" + prompt.name)] = prompt.MultipleAllowed ? "[]" : string.Empty; } else { this.ReportData[("prompt" + prompt.name)] = "formula:" + prompt.Values.StringRepresentation; } } ReportData.PromptOnRefresh = promptRefresh; if (promptRefresh) { FailurePageReceiver pageReceiver = new FailurePageReceiver((e) => { PromptDialogForm promptDialog = this.promptDialog; if (promptDialog == null) { return; } if (e == null) { MessageBox.Show("Invalid server response during prompt data refresh"); promptDialog.Close(); promptDialog = null; } else if (e is ViewerException && (((ViewerException)e).NeedPrompts)) { promptDialog.MergePromptData( ((ViewerException)e).Prompts); } else { promptDialog.Close(); ShowError(e); promptDialog = null; } }); ThreadManager.RequestPageData(this, rptDataCache, 1, true, new PageLoader(new Graphics2DPainter(false), pageReceiver), (a, b, c, d) => { }); } else { contentView.UpdatePageContent(true); StartProgress(); ThreadManager.RequestPageCount(this, rptDataCache, SetPageCount); groupTreeView.SetupTreeView(); } } /// /// /// public virtual void ShowError(Exception e) { if (e is ViewerException && ((ViewerException)e).Canceled) { // reset timestamp to force reload of group tree with the next page lastTimestamp = 1; // don't show exceptions occured during a cancelation return; } // since this method is used as delegate, it can be called any thread. if (InvokeRequired) { Invoke(new Action(ShowError), new object[] { e }); return; } try { contentView.LoadFailed = true; SetProgressStatus(Progress.ProgressStatus.Error); if (e is ViewerException && (((ViewerException)e).NeedPrompts || ((ViewerException)e).Message.Contains("NeedsPrompts"))) { PromptData[] promptData = ((ViewerException)e).Prompts; if (promptData != null) { if (promptDialog == null) { promptDialog = new PromptDialogForm(new List(promptData), new PromptPanelBuilder(), EnteredPrompts); promptDialog.ShowDialog(); } else if (!promptDialog.Visible) { promptDialog.MergePromptData(promptData); promptDialog.ResetDialog(); promptDialog.ShowDialog(); } } return; } if (!Visible) { // Only print the error message to the console when the view is not visible anylonger. Console.WriteLine(e); return; } // only one Error message for each ReportView if (box == null) { box = new MyMessageBox(); } if (!box.Visible) { box.Show(e); } } catch (Exception e1) { Console.WriteLine(e1); throw; } } /// /// /// public void DataRefresh() { if (this.contentView != null) { ReportData.PromptOnRefresh = origPromptOnRefresh; contentView.UpdatePageContent(true); StartProgress(); if (pageCount == -1) { ThreadManager.RequestPageCount(this, rptDataCache, SetPageCount); } } } /// /// This property indicates if the loaded report wasen't rendered completely due to the page limit on the server /// /// public bool PageLimitExceeded { get { return isPageLimitExceeded; } set { if (isPageLimitExceeded != value) { isPageLimitExceeded = value; OnPageChanged(); } } } /// /// /// public ReportInfo ReportInfo { get { return reportInfo; } set { reportInfo = value; if (reportInfo != null && string.IsNullOrEmpty(ReportData.ReportTitle)) { reportTitle = reportInfo.Title; if (string.IsNullOrEmpty(reportTitle)) { reportTitle = ReportInfo.FileName; } } else { reportTitle = ReportData.ReportTitle; } if (tabPage != null && reportTitle != null) { //Restrict the title to 30 characters string tabTitle = reportTitle; if (tabTitle.Length >= 30) { tabTitle = tabTitle.Substring(0, 27) + "..."; } tabPage.InvokeIfRequired(() => { if (!tabLabelDefined) { tabPage.Text = tabTitle; } }); } } } /// /// /// public bool ReportSuppressed { get { return reportInfo.IsReportSuppressed; } } /// /// /// public IReportViewer ReportViewer { get { return reportViewer; } set { reportViewer = (ReportViewer)value; } } /// /// /// /// /// /// public Progress Export(ExportFormat format, string file) { Dictionary parameters = new Dictionary(); parameters["file"] = file; parameters[URLRenderData.ParameterExportFmt] = ExportProgress.ExportFormatToString(format); return Export(parameters); } /// /// /// /// /// public Progress Export(Dictionary exportParameter) { ExportProgress export = new ExportProgress(this.ShowError, this.ReportData, exportParameter); return export; } /// /// Method needed for Unit testing /// internal virtual void ThreadFinished() { } /// /// /// public void Print() { if (PageInfo == null) { return; } PrinterSettings settings = reportViewer.PrinterSettings; PrintProgress a = new PrintProgress(ReportDataCache, settings, this.ShowError); a.CurrentViewPage = CurrentPage; a.PageInfo = contentView.PageInfo; a.ReportInfo = reportInfo; a.UpdatePageSettings(); if (pageCount > 0) { settings.MinimumPage = 1; settings.MaximumPage = PageCount; settings.FromPage = 1; settings.ToPage = PageCount; } else { settings.MinimumPage = 0; settings.MaximumPage = 9999; settings.FromPage = 0; settings.ToPage = 0; } settings.PrintRange = PrintRange.AllPages; printDialog.PrinterSettings = settings; printDialog.AllowSomePages = true; printDialog.AllowCurrentPage = true; printDialog.AllowPrintToFile = true; // UseEXDialog is required on Win7-64bit with .net 3.5, see http://stackoverflow.com/questions/1741302/printdialog-showdialogthis-immediately-returns-dialogresult-cancel-on-windows?rq=1 printDialog.UseEXDialog = true; if (printDialog.ShowDialog() == DialogResult.OK) { reportViewer.ToolBar.AddProgress(a); a.StartProgress(); } } /// /// /// public void OpenExportDialog() { ExportDialog exportDialog = reportViewer.ExportDialog; exportDialog.MultiPageReport = pageCount > 1; exportDialog.HasGroups = groupTreeView.SplitContainer != null && !groupTreeView.SplitContainer.Panel1Collapsed; exportDialog.ReportInfo = reportInfo; if (exportDialog.AnyFormatAvailable) { if (exportDialog.ShowDialog() == DialogResult.OK) { Progress progress = Export(exportDialog.CreateExportParameters()); reportViewer.ToolBar.AddProgress(progress); progress.StartProgress(); } } else { MessageBox.Show(strings.Export_NoFormats); } } /// /// Gets or sets the page view mode. /// public Viewer.PageViewMode PageViewMode { get { return contentView.PageViewMode; } set { contentView.PageViewMode = value; } } /// /// Sets the zoom mode. /// public ZoomMode ZoomMode { get { return contentView.ZoomMode; } set { contentView.ZoomMode = value; } } /// /// Sets the zoom factor (1.0 correspondes to original size). /// public float ZoomFactor { get { return contentView.ZoomFactor; } set { contentView.ZoomFactor = value; } } /// /// Sets the minimum zoom factor. /// public float ZoomMin { get { return contentView.ZoomMin; } set { contentView.ZoomMin = value; } } /// /// Sets the maximum zoom factor. /// public float ZoomMax { get { return contentView.ZoomMax; } set { contentView.ZoomMax = value; } } /// /// /// public new void Focus() { contentView.Focus(); } /// /// Method is called when this view was removed from the viewer. Cancels /// a loading progress if present and stops the ping timer. /// internal void OnRemoved() { pingTimer.Stop(); SetProgressStatus(Progress.ProgressStatus.Error); ReportData.Stop(); } /// /// Sets the status of the current progress. /// /// private void SetProgressStatus(Progress.ProgressStatus status) { Progress progress = this.progress; if (progress != null) { progress.Status = status; } } /// /// Sets the label of the tab of this view. /// public string TabLabel { set { tabLabelDefined = value != null; tabPage.Text = value; } } /// /// Gets the content view. /// internal ReportContentView ContentView { get { return contentView; } } } }