0
  • I have a Report Generator Form that has two dates

    From and To:

    When entered from and to dates and button click to generate report it goes into:

    private void generateReport_Click

    below: is an instance of my datagridview

    TR = Application.OpenForms.OfType().ElementAt(0);

    I'm taking all the data from my datagridview and converting into data-table.

    then XML writes this datatable into transactionreport.xml file,

    this file then loads up into crystal report to display report.

    I then dispose of my report.

Basically I want to show user, how much time it is taking to generate a report by creating background worker and reporting progress with progress bar, however this is giving me cross-thread exception.

public partial class TransactionRptGenForm : Form
    {
        Records  TR = new Records();
        public TransactionRptGenForm()
        {
            InitializeComponent();

        }
        public DataSet2 ds;
        public System.Data.DataTable dt;

        private void generateReport_Click(object sender, EventArgs e)
        {
            // Start the BackgroundWorker.
            backgroundWorker1.RunWorkerAsync();

        }

        private void TransactionRptGenForm_Load(object sender, EventArgs e)
        {
            this.MaximizeBox = false;
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            backgroundWorker1.ReportProgress(5);

            TR = Application.OpenForms.OfType<Records>().ElementAt(0);
            // Report Form.
            TReportDisplay TRD = new TReportDisplay();
            Treport treport1 = new Treport();
            backgroundWorker1.ReportProgress(10);

            try
            {                    
                ds = new DataSet2();
                dt = new System.Data.DataTable();
                dt = ConvertDGVtoDataTable(TR.transcationTableDataGridView);
                ds.Tables.Add(dt);
                backgroundWorker1.ReportProgress(20);

                try
                {  
                    ds.WriteXmlSchema("TransactionReport.xml");
                    treport1.SetDataSource(ds);
                    TRD.crystalReportViewer2.ReportSource = treport1;
                    backgroundWorker1.ReportProgress(40);

                    ParameterFieldDefinitions Parameters;
                    ParameterFieldDefinition Parameter;
                    ParameterValues Values = new ParameterValues();
                    ParameterDiscreteValue DiscreteValue = new ParameterDiscreteValue();

                    backgroundWorker1.ReportProgress(60);
                    DiscreteValue.Value = dateTimePicker1.Text;
                    Parameters = treport1.DataDefinition.ParameterFields;
                    Parameter = Parameters["fromdate"];
                    Values = Parameter.CurrentValues;

                    Values.Clear();
                    Values.Add(DiscreteValue);
                    Parameter.ApplyCurrentValues(Values);
                    backgroundWorker1.ReportProgress(70);

                    DiscreteValue.Value = dateTimePicker2.Text;
                    Parameters = treport1.DataDefinition.ParameterFields;
                    Parameter = Parameters["todate"];
                    Values = Parameter.CurrentValues;
                    backgroundWorker1.ReportProgress(80);

                    Values.Add(DiscreteValue);
                    Parameter.ApplyCurrentValues(Values);

                    backgroundWorker1.ReportProgress(100);
                    TRD.ShowDialog();
                    TR.Dispose();

                }
                catch (CrystalReportsException)
                {

                    { MessageBox.Show("Please install crystal reports runtime to view reports.", "Crystal Reports Missing"); }
                }
                catch (Exception ex) { MessageBox.Show(ex.Message.ToString()); };
            }

            catch (Exception ex) { MessageBox.Show(ex.Message.ToString()); }
        }

// This is the method that converts DataGridView into DataTable.

        public static System.Data.DataTable ConvertDGVtoDataTable(DataGridView DGV)
        {
            System.Data.DataTable dt = new System.Data.DataTable();
            dt.Columns.Add("ID", typeof(Int16));
            dt.Columns.Add("Transaction Type", typeof(string));
            dt.Columns.Add("Transaction Details", typeof(string));

            foreach (DataGridViewRow dgv in DGV.Rows)
            {
                dt.Rows.Add(dgv.Cells[0].Value, dgv.Cells[1].Value, dgv.Cells[2].Value);
            }
            return dt;
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

        }

    }
}

please explain what does this.invoker method do, what are thread unsafe calls and safe calls.

Patrick
  • 217
  • 1
  • 5
  • 19
  • Need help this will help me understand cross thread exception, every other cross thread solution is different from other :( – Patrick Oct 24 '16 at 07:54
  • You should start by removing all of the `catch (Exception ex)`. It's a bad anti-pattern to just catch all exceptions like that. – Enigmativity Oct 26 '16 at 04:59
  • got it. @Enigmativity please tell me how do I notify user if they are missing crystal report, it just throws an error and doesn't catch the crystal report missing exception. I have fixed my cross-thread exception issue by running on a different thread, but I still do not understand the invoker method concept. – Patrick Oct 26 '16 at 14:07
  • If you have a specific exception that you know may occur and that you can recover from then catch that exception (like you have with `CrystalReportsException`), but if you're catching `Exception` then you're creating bugs. If you have anything to report to the user then do the catch much higher up the invocation chain. – Enigmativity Oct 26 '16 at 21:38
  • The `.Invoke(...)` call on UI elements is used to push the execution of the code onto the UI thread - it's kind of like "Hey, UI, please do this when you can". – Enigmativity Oct 26 '16 at 21:39
  • I got it, but I don't know how to use this invoke method in my case. – Patrick Oct 27 '16 at 12:55
  • You don't appear to need to use invoke in your code, except maybe to display the dialog. The problem I think you're having is with `dt = ConvertDGVtoDataTable(TR.transcationTableDataGridView);` - that's reading data from a UI control on another thread. Try loading your `DataTable` before you call the thread and then pass the `DataTable` as a parameter. – Enigmativity Oct 27 '16 at 13:08

0 Answers0