0

all.

I know this issue is not new here. I found several similar ones (here, here, here and here) during my research, but I still could not make it work on my "to do list" like program.

Since it is a short term planning task manager, I chose to save the tasks in a XML file and I import all the data from it to the DataGridView. Based on the priority value (0 is low, 1 is normal and 2 is high) I want to show the corresponding picture on the respective cell of a DataGridViewImageColumn for each task.

I have already used breakpoints and watches to check if the logic is wrong, but I could not find any issue with it. It is correctly reading the priority values and choosing the correct option inside the switch statement. I loaded these three images as resources, but they are not being shown in the DataGridViewImageCells.

enter image description here

Column 0 is hidden in the dataGridView. Column 1 (header is "P" in the picture) has the task priority values.

Below follows the code excerpt (XMLReader is a class I wrote to import the data):

    XMLReader tasks = new XMLReader("tasks.xml");
    tasks.Open();
    statusBarLabel.Text = tasks.TaskCount.ToString() + " tasks listed - Priority: " +
        tasks.TaskLowCount.ToString() + " low, " + tasks.TaskNormalCount.ToString() +
        " normal, " + tasks.TaskHighCount.ToString() + " high";

    DataSet dataset = new DataSet();
    dataset.ReadXml("tasks.xml");
    dataGridView.DataSource = dataset.Tables[0];
    dataGridView.Columns[1].HeaderText = "P"; // Priority level
    dataGridView.Columns[2].HeaderText = "Due to"; // Task due date
    dataGridView.Columns[3].HeaderText = "Description"; //Task description
    dataGridView.Columns.Add(new DataGridViewImageColumn());
    for (int i = 0; i <= dataGridView.Rows.Count - 1; i++)
    {
        switch (dataGridView.Rows[i].Cells[1].Value.ToString())
        {
            case "0":
                dataGridView.Rows[i].Cells[4].Value = Properties.Resources.Low_16x;
                break;
            case "1":
                dataGridView.Rows[i].Cells[4].Value = Properties.Resources.Normal_16x;
                break;
            case "2":
                dataGridView.Rows[i].Cells[4].Value = Properties.Resources.High_16x;
                break;
            default:
                dataGridView.Rows[i].Cells[4].Value = null;
                break;
        }
    }

What am I doing wrong?

I appreciate any help.

UPDATE:

As I mentioned in the comments below, I noticed something strange in the Resources folder that I have never seen before. I removed the images from the solution resources, keeping only one of them that is in use in a button. After that I added to the solution resources only one of the three images I want to display in the DataGridViewImageCells and the screenshot below shows the status of evertything after adding the image to it.

enter image description here

In this screenshot you can see that the image exists in the solution resources, but it is not being copied to the solution Resources folder. I also captured the File Explorer opened showing the folder contents to illustrate it better.

I believe this may be the problem that is not allowing me to properly show the images inside the DataGridViewImageColumn cells.

Any clues on how to solve it? It did not happen in a test solution I created to check if it would work (and it worked - you can check it here).

Marcelo C.
  • 23
  • 7
  • Set a breakpoint in the switch to make sure values are actually being hit/set which may have tried but didn't indicate. – Karen Payne Apr 16 '22 at 15:18
  • I have already done it. The correct priority value is being identified and after executing the proper case statement, the cell content changes from null to bitmap, but the image is not being shown. – Marcelo C. Apr 16 '22 at 15:37
  • Are you 100% sure that the images will display properly? Have you tried to add the images into something like a `PictureBox`? … The posted code appears to work as expected and I would check to see if the images will display properly in any other control. – JohnG Apr 16 '22 at 20:08
  • Check [the following](https://github.com/karenpayneoregon/q-a-questions/tree/master/DataGridViewConditionalImagesDataGridView) out which uses a different approach for the same result. The key is using a BindingSource and invoking ResetBindings for the BindingSource. – Karen Payne Apr 16 '22 at 20:56
  • @JohnG I did it. Just for testing purposes, I added three different PictureBoxes in the form and was able to display all the three images on them. :-/ – Marcelo C. Apr 16 '22 at 21:02
  • Hey, @KarenPayne. Thank you for sharingg the project in your Github. However, although I confirmed that the image files are in Resources folder, when I run your code it doesn't show them in the DataGridView, i.e. the same behavor I observe in my solution. Did you tested it before sending me? Did it work with you? Which version of Visual Studio are you working on? 2022 as mine? – Marcelo C. Apr 16 '22 at 21:23
  • How are you adding the images to the project? And how did you check the images in the `PictureBox` as per my previous comment? In other words, did you use the same path as shown in your current code? I have copy/paste your code and it works as expected in my small tests. – JohnG Apr 16 '22 at 21:53
  • I just noted something weird. In Solution Explorer, on Resources folder, the image files are not listed. However, if I right-click on Resources folder and ask VS to open it in File Explorer, I can see them there. I created a separate test solution and I was able to make it work with a more simple code just to show the images, but in this case the image files are being listed inside Resources folder in Solution Explorer. Does it have something to do with the problem I described in the original question? I shared this test solution [here](https://github.com/mcutin/DataGridViewImageTest). – Marcelo C. Apr 16 '22 at 21:56
  • @JohnG I right-clicked in the project and chose Properties. After that, I clicked on Resources on the left side of the properties window and added it as an existing image. I executed this same steps in the test solution I mentioned in the comment above and it worked. Concerning the `PictureBoxes`, I added them in the code refererring the images themselves. I can't remember right now if I referred to the images inside the Resources or directly to the files, but I can test it again. No problem. – Marcelo C. Apr 16 '22 at 22:01
  • I can not reproduce what you describe using your posted code. I got the same images you posted on GIT but they also worked as expected. Also, it appears you are adding the images correctly and I can only assume something else must be going on. – JohnG Apr 16 '22 at 22:12
  • @JohnG thank you so much for your time and patience. Please take a look at the original question up there, since I updated it with some relevant information. Have you seen what I described in this update happening before? – Marcelo C. Apr 16 '22 at 22:26
  • @MarceloC. I never post code without first ensuring the code works. go back to the GitHub repository, I posted a screenshot. The code was done in VS2019 and see zero reasons for the code to fail for .NET Framework classic or .NET Core Framework. – Karen Payne Apr 16 '22 at 22:27
  • @KarenPayne Sorry if I made you think I criticized you. It was not my intention. Thank you so much for your help. – Marcelo C. Apr 16 '22 at 22:52
  • Well… the posted image clearly shows that the Low, Normal, High images are NOT getting added correctly. You can see them in the project but not in the resources folder. I suggest you dump/delete all the images from the solution and re-add them as you described. If they do not show up in the resources folder… then this sounds like a VS issue or setting. – JohnG Apr 16 '22 at 22:53
  • In other words, the picture clearly shows some disconnect between what the project tree shows in its resources folder and what the resources folder actually contains… unless the folder you show is not the same folder in the project. It is not clear what the full path is to the folder in the picture. – JohnG Apr 16 '22 at 22:54
  • @JohnG I can confirm the folder shown in File Explorer in the screenshot is the same Resource folder shown in the solution in VS because I opened it by right-clicking on the folder name in the Solution Explorer and then choosing "Open in File Explorer". Just as an update, I removed all the references for these three image files from the project and then added them again. Now they are appearing inside the Resource folder as well as they now have their own entries on solution's csproj file (it wasn't happening before). I still cannot make it work here, even though. I'll keep digging... – Marcelo C. Apr 16 '22 at 23:53

1 Answers1

0

I made it work! I thought about not using the code shared in the question, which loads the XML file content inside a Dataset which, in turn, was used as the DataGridView's data source. Instead of this approach, I tried to programmatically load each of the tasks into the DataGridView.

I made it by using an XMLReader object instantiated based on a class that I had previously created for this solution. This class has a Content property that holds all the data from inside the XML file opened by using its Open method. Here is the XMLReader Open method code:

public bool Open()
{
    try
    {
        this.Content = XElement.Load(this.File);
        this.TaskCount = UpdateTaskCount();
        this.TaskHighCount = PriorityTaskCount(2);
        this.TaskLowCount = PriorityTaskCount(0);
        this.TaskNormalCount = PriorityTaskCount(1);
    }
    catch (FileNotFoundException e)
    {
        MessageBox.Show("File not found: " + e.FileName + "\nSystem message: " + e.Message,
            "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return false;
    }
    catch (IOException e)
    {
        MessageBox.Show("Unknown I/O exception.\nError code: " + e.HResult +
            "\nSystem message: " + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return false;
    }
    return true;
}

Content property sets a content field, which is of XElement type.

With the XML file data loaded on it, I replaced the previous approach by the following one (also changed the DataGridView name):

XMLReader tasks = new XMLReader("tasks.xml");
tasks.Open();
statusBarLabel.Text = tasks.TaskCount.ToString() + " tasks listed - Priority: " +
    tasks.TaskLowCount.ToString() + " low, " + tasks.TaskNormalCount.ToString() +
    " normal, " + tasks.TaskHighCount.ToString() + " high";

dgvTasks.Columns.Add(new DataGridViewImageColumn()); // Col 0 - Priority image
dgvTasks.Columns.Add(new DataGridViewTextBoxColumn()); // Col 1 - Due date
dgvTasks.Columns.Add(new DataGridViewTextBoxColumn()); // Col 2 - Description

dgvTasks.Columns[0].Width = 25;
dgvTasks.Columns[1].Width = 100;

IEnumerable<XElement> allTasks = tasks.Content.Descendants("task");
foreach (var task in allTasks)
{
    dgvTasks.Rows.Add();                
    DataGridViewImageCell prioImg = (DataGridViewImageCell)dgvTasks.Rows[dgvTasks.Rows.Count - 1].Cells[0];
    DataGridViewTextBoxCell date = (DataGridViewTextBoxCell)dgvTasks.Rows[dgvTasks.Rows.Count - 1].Cells[1];
    DataGridViewTextBoxCell description = (DataGridViewTextBoxCell)dgvTasks.Rows[dgvTasks.Rows.Count - 1].Cells[2];
    Int32.TryParse(task.Element("priority").Value, out int priority);
    switch (priority)
    {
        case 0:
            prioImg.Value = Properties.Resources.Low;
            break;
        case 1:
            prioImg.Value = Properties.Resources.Normal;
            break;
        case 2:
            prioImg.Value = Properties.Resources.High;
            break;
        default:
            prioImg.Value = null;
            break;
    }
    date.Value = task.Element("dueDate").Value;
    description.Value = task.Element("description").Value;
}

And here it is:

enter image description here

Once more, thank you so much Karen Payne and JohnG for your support. Your insights made me think in the correct direction, although the solution was not based on the original approach.

Marcelo C.
  • 23
  • 7