Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

winform picturebox image shows empty c#

I have a form that contains a picturebox. When the form loads the default image loads fine. I then update the image when something in my form changes that changes the image being displayed. The generation of this image works fine also, I can see the image on disk and open it with paint, etc. In general what I do is open a file stream on the image location and then set the image to this location.

 if (this.picPreview.Image != null)
 {
    this.picPreview.Image.Dispose();
    this.picPreview.Image = null;
 }
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
this.picPreview.Image = System.Drawing.Image.FromStream(fs);

But no matter what I do the image appears blank on the form. I've tried refreshing the form, refreshing the picturebox control, setting it's visible property to visible, nothing helps.

I create a separate form which contains only a picturebox and pass the image location to the form and repeat the process of opening the stream and then setting the image to this location it works perfectly.

No exceptions are being throw AFAIK...debugger is set to break on all exceptions.

What might cause this behavior?

Any advice is appreciated. I have another application that does image generation in a backgroundworker thread and that works fine also.

Maybe providing more background about what I'm trying to do will help me get to the bottom of this. For every row in my datagridview there are between one and three columns that have an image associated with them. I've generated all these images in advance. Scrolling through the grid I get a preview image of the first image column in my picture box using the SelectionChanged event. It works perfectly. I also have cells that when clicked on show a form window with a preview of the images which make up the image on the main form. This also works perfectly. I can change rows and click on cells in the grid and everything works. Basically I'm constructing a new image based on what the user selects on other controls which are bound to the datagrid.

The problem arises when I attempt to change the image in the picture box on the main form. I can update the datasource, and see the grid values update, but the image, which I now regenerate using third party software, which I can verify is on the disk and is viewable after the update takes place, just vanishes. Once this happens, I no longer get an image in the picture box in the form until I close the form and reopen, and then all the updated data is there and everything works again. The code that gets called on selection changed to set the image is exactly the same as the code in place to update the new image. It is completely synchronous. I'm running out of ideas other than starting from scratch with a whole new form.

Thanks again for all suggestions.

I'll start from the top. The overall flow is as follows:

Open a form which contains a datagrid which is bound to a SQL view. The dgv is read-only, and only one row can be selected at a time. The view populates automatically, along with controls which are bound to each column of the grid. These include text boxes for some, comboboxes and checkboxes for the others. Each row has a set of images associated with it. When the form loads, I can scroll down the view and for each row a new image appears in the picture box on the form. All these images have been pregenerated. When a row is selected, there may be up to three images for the row, in which case navigation buttons are enabled to allow the user to preview each image.

I select a row, change the controls on the form to change one or more cell values in the selected row. I then hit a save button, which updates the database and the corresponding data in the grid. I then attempt to update the image(s) for this row. At this point the picture box disappears and I lose the previews on the form all together; no images appear until I close and reopen the form, and all is well again until I do a save.

In trying to solve this I find that updating a bound dgv causes the selectchanged event to be raised multiple times. There is code to handle cases where the binding is not complete, or there is nothing selected in the view. There is also code in the btnSave_Click handler to suspend the selectionchanged event handler until the update is complete and the image(s) are regenerated. Despite doing this, even though the row I updated is selected in the view, the actually selected row (where the arrow is positioned, and what all the controls display) the first row is always the "current" row after the update. I don't know how to fix that yet. Here is the code for the selection changed and the button save event handlers.

Here is a screen shot of the form:enter image description here

And the code for the selectionchanged and btn_save event handlers:

/// <summary>
        /// update the preview on a row selection change
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataGridView1_SelectionChanged(object sender, EventArgs e)
        {
            if (!BindingComplete) return;

            DataGridView dgv = (DataGridView)sender;

            if (!dgv.Focused || dgv.CurrentRow == null) return;         

            // set the pic preview to the current row image(s)
            // we need the record for the current index
            DataRowView currentDataRowView = (DataRowView)dgv.CurrentRow.DataBoundItem;

            if (currentDataRowView == null) return;

            DataRow currentRow = currentDataRowView.Row;

            LastSelectedIndex = dgv.SelectedRows[0].Index;

            Debug.WriteLine("Current row in SelectionChanged: " + currentRow.ItemArray[0].ToString());

            bool showBox = false, showProd = false, showWire = false;
            string box, prod, wire;

            string pdcProductName = currentRow.ItemArray[0].ToString();

            showWire = !string.IsNullOrEmpty(wire = currentRow.ItemArray[7].ToString());

            showBox = !string.IsNullOrEmpty(box = currentRow.ItemArray[8].ToString());

            showProd = !string.IsNullOrEmpty(prod = currentRow.ItemArray[9].ToString());

            // check for wirepath, box, and product. Enable the nav buttons if there is more than 
            // one label for this product. We need to check for LabelFileName being the same for both
            // box and product, in which case there is one file for both which defaults to box
            if ((showBox && showProd && prod == box) || showBox)
            {
                string targetFile = PreviewImagePath + pdcProductName + "_eBox.png";

                if (picPreview.Image != null)
                {
                    //picPreview.Image.Dispose();
                    //picPreview.Image = null;
                }

                // if the preview image doesn't exist yet use a default image
                if (!File.Exists(targetFile))
                {
                    // make the loading gif invisible
                    this.picLoading.Visible = true;

                    //picPreview.Image = AdminTILE.Properties.Resources.StandardPaper;
                }
                else
                {
                    this.picLoading.Visible = false;
                    Debug.WriteLine("Opening file " + targetFile);

                    FileStream fs = new FileStream(targetFile, FileMode.Open, FileAccess.Read);
                    picPreview.Image = System.Drawing.Image.FromStream(fs);
                    Image imgCopy = (Image)picPreview.Image.Clone();
                    this.picPreview.Visible = true;
                    fs.Close();

                    // preview in another frame
                    if (frm.IsDisposed)
                    {
                        frm = new PreviewImage();
                    }
                    frm.PreviewLabel(imgCopy);
                    frm.Show();                 

                    //picPreview.ImageLocation = targetFile;
                }
            }            
            else if (showProd)
            {
                string targetFile = PreviewImagePath + pdcProductName + "_eBox.png";

                if (picPreview.Image != null)
                {
                    picPreview.Image.Dispose();
                    //picPreview.Image = null;
                }

                if (!File.Exists(targetFile))
                {
                    // make the loading gif invisible
                    this.picLoading.Visible = true;
                    //picPreview.Image = AdminTILE.Properties.Resources.StandardPaper;
                }
                else
                {
                    this.picLoading.Visible = false;
                    FileStream fs = new FileStream(targetFile, FileMode.Open, FileAccess.Read);
                    picPreview.Image = System.Drawing.Image.FromStream(fs);
                    fs.Close();
                }
            }           

        }


        /// <summary>
        /// update the database with the current selections
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSave_Click(object sender, EventArgs e)
        {

            if (dataGridView1.SelectedRows.Count == 0)
            {
                MessageBox.Show("No record is selected to update");
                return;
            }

            DialogResult result1 = MessageBox.Show("Saving Label Configuration. Are you sure?",
                "IMPORTANT!", MessageBoxButtons.YesNoCancel);

            // update the view
            if (result1 == DialogResult.Yes)
            {

                // we need the record for the current index
                DataRowView currentDataRowView = (DataRowView)dataGridView1.CurrentRow.DataBoundItem;
                DataRow currentRow = currentDataRowView.Row;                
                string pdcProductName = currentRow.ItemArray[0].ToString();



                Int32 currentIndex = dataGridView1.SelectedRows[0].Index;

                Debug.WriteLine("Current index in Save:" + currentIndex.ToString());

                string AgencyId="", LogoId="", WireId="";

                SqlDataAdapter LabeledProductsDataTableAdapter =
                new SqlDataAdapter("SELECT * FROM LabeledProducts",
                    printConfigTableAdapter.Connection);

                SqlDataAdapter LogosDataTableAdapter =
                new SqlDataAdapter("SELECT * FROM Logos",
                    printConfigTableAdapter.Connection);

                if (vwTILEAdminTableAdapter.Connection.State != ConnectionState.Open)
                {
                    printConfigTableAdapter.Connection.Open();
                }

                DataTable LogoDataTable = new DataTable();
                LogosDataTableAdapter.Fill(LogoDataTable);

                DataTable LabeledProductsDataTable = new DataTable();
                LabeledProductsDataTableAdapter.Fill(LabeledProductsDataTable);

                StringBuilder sql = new StringBuilder();

                // Fill a table with the results of the 
                // data adapter and query the table instead of the database.
                // An empty LogoDescription maps to an empty filename
                DataRow dataRow;

                if (cbAgency.SelectedItem != null)
                {
                    sql.Append("LogoDescription = '").Append(cbAgency.SelectedItem).Append("'");                       
                    dataRow = LogoDataTable.Select(sql.ToString())[0];
                    AgencyId = dataRow.ItemArray[0].ToString();

                    sql.Clear();
                }

                if (cbPrivateLabel.SelectedItem != null)
                {
                    sql.Append("LogoDescription = '").Append(cbPrivateLabel.SelectedItem).Append("'");
                    dataRow = LogoDataTable.Select(sql.ToString())[0];
                    LogoId = dataRow.ItemArray[0].ToString();

                    sql.Clear();
                }

                if (cbWire.SelectedItem != null)
                {

                    sql.Append("LogoDescription = '").Append(cbWire.SelectedItem).Append("'");
                    dataRow = LogoDataTable.Select(sql.ToString())[0];
                    WireId = dataRow.ItemArray[0].ToString();

                    sql.Clear();
                }


                // PdcProductName is the primary key
                sql.Append(@"UPDATE [dbo].[LabeledProducts]
                    SET [PdcProductName] = @pdcProd
                        ,[LabelProductName] = @lblProd
                        ,[LabelDescription] = @lblDesc
                        ,[Power] = @pwr
                        ,[Fabrication] = 0
                        ,[UL_File_Number] = @ul
                        ,[PrePrintedSerial] = @pps
                        ,[ShowOrderOnLabel] = 0
                        ,[PrivateLabelLogoId] = @plid
                        ,[AgencyImageId] = @aid
                        ,[WireDiagConfigId] = @wid
                        ,[ReleasedForProduction] = @rfp
                    WHERE PdcProductName = '").Append(pdcProductName).Append("'");

                using (SqlCommand command = new SqlCommand(sql.ToString(), vwTILEAdminTableAdapter.Connection))
                {
                    if (vwTILEAdminTableAdapter.Connection.State != ConnectionState.Open)
                        vwTILEAdminTableAdapter.Connection.Open();

                    LabeledProductsDataTableAdapter.UpdateCommand = command;
                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@pdcProd", txtPdcProdName.Text);
                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@lblProd", txtLabeledProd.Text);
                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@lblDesc", txtLabelDesc.Text);
                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@pwr", txtPower.Text);                       
                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@ul", txtULFileNumber.Text);
                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@pps", cbPrePrintedSerial.Checked);

                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@plid", LogoId);
                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@aid", AgencyId);
                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@wid", WireId);
                    LabeledProductsDataTableAdapter.UpdateCommand.Parameters.AddWithValue("@rfp", cbReleased.Checked);

                    //int rowsAffected = LabeledProductsDataTableAdapter.Update(LabeledProductsDataTable);  
                    int rowsAffected = command.ExecuteNonQuery();

                    // The DataViewManager returned by the DefaultViewManager
                    // property allows you to create custom settings for each
                    // DataTable in the DataSet.
                    DataViewManager dsView = this.tILEDataSet.DefaultViewManager;

                    // remove the selectionChanged event handler during updates
                    // every update causes this handler to fire three times!!!
                    this.dataGridView1.SelectionChanged -= new System.EventHandler(this.dataGridView1_SelectionChanged);


                    dataGridView1.DataSource = typeof(TILEDataSet.vwTILEAdminDataTable);
                    this.vwTILEAdminBindingSource.DataSource = typeof(TILEDataSet.vwTILEAdminDataTable);
                    this.vwTILEAdminBindingSource.DataSource = this.tILEDataSet.vwTILEAdmin;
                    this.dataGridView1.DataSource = this.vwTILEAdminBindingSource;                     
                    vwTILEAdminBindingSource.ResetBindings(false); // false for data change, true for schema change

                    this.vwTILEAdminTableAdapter.Fill(this.tILEDataSet.vwTILEAdmin);

                    // we need to reget the row after the update to pass to preview
                    currentIndex = LastSelectedIndex;
                    DataGridViewRow drv = this.dataGridView1.Rows[currentIndex];
                    currentRow = ((DataRowView)(drv.DataBoundItem)).Row;

                    // update the preview files

                    UpdatePreviewFiles(currentRow);


                    // try this
                    dataGridView1.ClearSelection();
                    // this doesn't work
                    dataGridView1.Rows[currentIndex].Selected = true;


                    // reset the selection changed handler once the update is complete
                    this.dataGridView1.SelectionChanged += new System.EventHandler(this.dataGridView1_SelectionChanged);

                }

            }
        }

After the update the form looks the same except the picture box is gone, and data from the first row shows up in the controls rather than the row that is highlighted.

AfterSave

All UpdatePreviewFiles does is replace the images with the updated ones. All ShowPreview does is set the image to the picturebox.Image. It all works until I do a save/update.

If there is anything else I can provide let me know, this is taking far too long to solve, I know there is a relatively simple explanation.

Thanks again.

like image 327
Gary Avatar asked Oct 08 '22 20:10

Gary


1 Answers

Try doing:

this.picPreview.Image = Image.FromFile(imagePath);

Instead of messing around with FileStream.

Also, you don't have to set this.picPreview.Image to null after disposing it. When you call dispose, it releases all resources regardless to whether you have a pointer to it or not.

Setting null to something, or in other and more accurate words, losing any reference (pointer) to an object - will cause the GC (Garbage Collector) to release it's resources.

Using the Dispose method will allow the GC to release it even though you still have a reference to it. (Thanks Rowland Shaw), so simply re-setting it to Image.FromFile(imagePath) will work just fine.

The reference to the previous image will be lost and the GC will dispose it when it feels like it (won't be long, I promise).

So to sum it up, I would suggest replacing the WHOLE piece of code with the single line in the beginning of this answer.

like image 130
SimpleVar Avatar answered Oct 10 '22 10:10

SimpleVar