Xtrareport 使用Savelayout及Loadlayout

来源:互联网 发布:python 求均值 方差 编辑:程序博客网 时间:2024/04/29 07:35

 http://community.devexpress.com/forums/p/54467/183602.aspx

 

I've been struggling with several issues with XtraReports and thought it would be nice to share my solutions.

The scene
We use a custom data source that implements all the required interfaces. We allow the creation of reports via the user designer and reports are saved to our database or a file. When viewing or editing reports we set the reports datasource at runtime.

We are using 7.1.2

The issues

  • On SaveLayout the datasource would be saved. This would end up being a broken version of our datasource
  • On LoadLayout extra datasource components would get added (broken) and bindings would reference the wrong datasource.
  • We need to be able to pass data to the report, such as title, filter string, image
  • Script references would get lost on loading, previewing etc. thus breaking Preview mode


The Solution

We use a subclass of the XtraReport to make our required alterations. Here it is...


    // A class to store datasource info while saving
    public class MyReportData
    {
        public object DataSource = null;
        public string DataMember = null;
        public Dictionary<string, object> Parameters = null;
        public object Tag = null;

    }

    // The modified XtraReport
    // Provides ability to add name/value parameters to the report (Parameters property)
    // IntialiseScriptReferences() will set any required script references as needed
    // ExtractData() and RestoreData(MyReportData) should be used around SaveLayout and LoadLayout
    // to ensure datasources dont get messed up
    public class MyReport : DevExpress.XtraReports.UI.XtraReport
    {

        public MyReport()
        {
            this.IntialiseScriptReferences();    
        }

        public void IntialiseScriptReferences()
        {
            // re-set the ScriptReferences
            // This example will reference all assemblies (not good for security!)
    
            List<String> assemblyLocations = new List<string>();

            foreach (Assembly assembly in System.AppDomain.CurrentDomain.GetAssemblies())
            {
                assemblyLocations.Add(assembly.Location);
            }

            this.ScriptReferences = assemblyLocations.ToArray();
        }

        public static event EventHandler ReportInitialized = null;
        protected override void OnReportInitialize()
        {
            // Cloans of reports are used to do previews. These cloans don't have the script references.
            // This will add the required references to the cloan before it gets used.

            // you should also have your designer listen to this event so it can call RestoreData()
            // To setup the datasource for the cloan. Otherwise the preview will have no data!

            base.OnReportInitialize();

            this.IntialiseScriptReferences();

            if (ReportInitialized != null)
                ReportInitialized(this, EventArgs.Empty);
        }

        protected MyReportData GetData()
        {
            // Gather the datasource data from the report
            MyReportData data = new MyReportData();
            data.DataSource = this.DataSource;
            data.DataMember = this.DataMember;
            data.Parameters = this._Parameters;
            data.Tag = this.Tag;

            return data;
        }


        protected void ClearData()
        {
            /// remove all datasource references in the report
            this.DataSource = null;
            this.DataMember = null;
            this._Parameters = null;
            this.Tag = null;

            RecursiveSetDataSources(null);
        }
        public MyReportData ExtractData()
        {
            // Gather the datasource data from the report then remove all datasource references
            // This should be called before a SaveLayout or LoadLayout so no datasource info is
            // included in the saved or loaded data

            MyReportData data = GetData();

            ClearData();

            return data;
        }

        public void RestoreData(MyReportData data)
        {            
            // re bind all references to the datasource
            // This should be called after a SaveLayout or LoadLayout to re-establish the datasouces            


            RecursiveSetDataSources(data.DataSource);

            this.DataSource = data.DataSource;
            this.DataMember = data.DataMember;
            this._Parameters = data.Parameters;
            this.Tag = data.Tag;

        }

        protected void RecursiveSetDataSources(object datasource)
        {
            this.RecursiveSetDataSources(datasource, this);
        }

        private void RecursiveSetDataSources(object datasource, XRControl control)
        {
            // Iterate all controls in the report to change their datasource
            // This assumes all bound controls are using the same datasource

            if (control is XtraReportBase)
            {
                XtraReportBase reportControl = (XtraReportBase)control;

                string member = reportControl.DataMember;
                if (!String.IsNullOrEmpty(member)) // only set datasource if a datamember has been specified
                {
                    reportControl.DataSource = datasource;
                    reportControl.DataMember = member; // reset member as setting datasource may null this out
                }
            }

            // also make sure all Bindings are updated
            foreach(XRBinding binding in control.DataBindings)
            {
                string member = binding.DataMember;
                if (!String.IsNullOrEmpty(member))
                {
                    binding.Assign(datasource,member);
                }
            }

            // Do the recursive bit
            if (control.HasChildren)
            {
                foreach (XRControl innerControl in control.Controls)
                {
                    RecursiveSetDataSources(datasource, innerControl);
                }
            }
        }


        private Dictionary<string, object> _Parameters = null;
        public Dictionary<string, object> Parameters
        {
            get
            {
                // this collection of data can be accessed via scripts in the report
                // so data can be passed into the report such as titles, images
                if (_Parameters==null)
                {
                    this._Parameters = new Dictionary<string, object>();
                }

                return _Parameters;
            }

        }
    }

    // This is a designer class modified so it will bind the datasource correctly when previewing
    // previewing uses a cloan of the report. We listent to the initialisation of all cloans, if its
    // a cloan of our report then we bind the datasource
    // To use this make sure you set the Report property
    public partial class MyXRDesignForm : DevExpress.XtraReports.UserDesigner.XRDesignFormExBase
    {
        private MyReport _Report = null;
        private string ReportTag = null;
        private MyReportData MyReportData = null;

        public MyXRDesignForm()
        {    
            InitializeComponent();

            // listen to all reports being initialised
            MyReport.ReportInitialized += new EventHandler(Report_ReportInitialized);
                
        }

        public MyReport Report
        {
            get
            {
                return _Report;
            }
            set
            {
                if (_Report != value)
                {
                    if (_Report != null)
                    {
                        _Report.Tag = null;

                    }

                    _Report = value;
                    if (_Report != null)
                    {
                        // get the datasource so we can bind cloaned reports to it
                        this.MyReportData = _Report.GetData();

                        // store tracking info to determine if a report is ours
                        this.ReportTag = "Report" + DateTime.Now.Ticks.ToString();
                        _Report.Tag = this.ReportTag;
                    }
                    else
                        this.MyReportData = null;
                }
            }
        }

        void Report_ReportInitialized(object sender, EventArgs e)
        {
            MyReport report = (MyReport)sender;

            // check if its our report. if so then bind the datasource
            if (this.ReportTag != null && report.Tag != null && this.ReportTag == report.Tag.ToString())
                report.RestoreData(this.MyReportData);
        }
    }


Whenever you do a SaveLayout or LoadLayout you should use this template...

MyReportData myReportData = null;
try
{
    myReportData = report.ExtractData();
    report.SaveLayout(stream);  // or report.LoadLayout(stream)
}
finally
{    
    report.RestoreData(myReportData);
}

 

I hope this is helpful

Disclaimer: The code is a modified version of the one we use. It has not been tested or compiled so it may contain bugs and syntax errors.

Tony McCreath