/*
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;
}
}
}
}