Picturebox, set and get property to take an image throwing null C# - c#

I have a picturebox that will filled up by OpenFileDialog() after that, I must render the histogram (chart) from it. I use the get and set property to take the image from picturebox to another class or form. But I always getting NullReferenceException. The bitmap seems not having the image after I open a image file, so it's returning nothing. I try to fill the bitmap parameter with full path of an image and it's working, but OpenFileDialog() become pointless.
Click options button:
to render histogram chart:
Here's my code
MainForm.cs
// button for opening image
private void openImage_Click(object sender, EventArgs e)
{
OpenFileDialog img = new OpenFileDialog();
img.Title = "Open Image File...";
img.Filter = "Image File (*.bmp, *.jpg, *.jpeg, *.png |*.bmp;*.jpg; *.jpeg;*.png";
if (img.ShowDialog() == DialogResult.OK) {
pbInput.Image = new Bitmap(img.FileName);
// blablabla
}
}
// set and get property
public Image getImage {
get { return pbInput.Image; }
set { pbInput.Image = value; }
}
OptionsForm.cs
private void hist1_Click(object sender, EventArgs e)
{
h1 = new Histogram();
h1.FormClosed += (s, a) => hist1.Enabled = true;
hist1.Enabled = false;
h1.Show();
}
Histogram.cs
public partial class Histogram : Form
{
MainForm m = new MainForm();
public Histogram()
{
InitializeComponent();
Bitmap b = new Bitmap(m.getImage);
//bla bla bla. . . . . *creating histogram code
}
}
The error message that I got:
I hope this question is clear enough. Thank you..!
PS: English is not my primary language, so apologize for my grammar, etc.

When you write MainForm m = new MainForm(); in your Histogram.cs,
You create a brand new object of Form that doesn't have reference to your old form's image
What you want is to be able to access old form's object reference in your new form or better get that image3 reference in Histogram.cs
One way to do it is to pass it to the constructor
Histogram button
private void hist1_Click(object sender, EventArgs e)
{
h1 = new Histogram(this.getImage);
h1.FormClosed += (s, a) => hist1.Enabled = true;
hist1.Enabled = false;
h1.Show();
}
and then your Histogram form
public partial class Histogram : Form
{
public Histogram(Image image)
{
Bitmap b = new Bitmap(image);
}
}
This will give you the Image in the histogram form.

You should set image to image property
getImage = new Bitmap(img.FileName);

Why don't you directly assign the image to your property?
public Image getImage { get; private set; } // Auto-implemented property.
like this
if (img.ShowDialog() == DialogResult.OK) {
getImage = new Bitmap(img.FileName);
pbInput.Image = getImage;
}
Btw., getImage is not a good name for a property. GetSomething is generally used for methods. Just call your property Image:
public Image Image { get; private set; }

Related

Create a picturebox_clicked event for each picturebox created

private Form1 form1 { get; set; }
readonly HttpClient httpClient = new();
public static string FolderPath = #".\Storage";
public static int picNum = 0;
public static List<string> outofScopeURL = new List<string>();
private async void Detailed_View_Load_1(object sender, EventArgs e)
{
DirectoryInfo directory = new(FolderPath);
FileInfo[] files = directory.GetFiles();
string[] FileFormats = { ".png", ".jpg", ".jpeg", ".txt" };
for (int pictureCreator = 0; pictureCreator < files.Length; pictureCreator++)
{
FileInfo file = files[pictureCreator];
if (file.FullName.EndsWith(FileFormats[3]))
{
string url = File.ReadAllText(file.FullName);
if (url != null)
{
try
{
Image gif = Image.FromStream(await httpClient.GetStreamAsync(url));
PictureBox picture = new PictureBox
{
Name = url,
Size = new(38, 38),
Image = gif,
Location = new((gif.Width * pictureCreator), 0),
SizeMode = PictureBoxSizeMode.Zoom
};
this.Controls.Add(picture);
MessageBox.Show(picture.Name);
outofScopeURL.Add(url);
picture.Click += Picture_Click;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
picNum++;
}
}
}
What I'm trying to achieve:
I'm trying to look in a specific directory for text files, then I want to read the files and extract the URL present in them.
After I got all of the URLs of all the text files present in my folder, I added them into a list so I could use them with their respective picturebox
For example, when URL1 has picture1 and URL2 has picture2, I want to set the text of the clipboard to URL1 when picturebox1 that has picture1 applied to it is clicked.
What I have tried: I have tried making a new picturebox_Clicked event in the pictureCreator for loop but the problem is that the event is always applied to the last picturebox created.
Any help is much appreciated.
Your question is about creating a picturebox_clicked event for each picturebox created.
The code you posted seems to be doing that but as mentioned in the comments, casting the sender argument would be the key to making event useful. Consider a click handler implemented similar to this:
private void onAnyPictureBoxClick(object? sender, EventArgs e)
{
if(sender is PictureBox pictureBox)
{
var builder = new List<string>();
builder.Add($"Name: {pictureBox.Name}");
builder.Add($"Tag: {pictureBox.Tag}");
builder.Add($"Image Location: {pictureBox.ImageLocation}");
MessageBox.Show(string.Join(Environment.NewLine, builder));
}
}
Now give it a test (simulating the part where the URLs are already collected from the local store).
public partial class MainForm : Form
{
public MainForm() =>InitializeComponent();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
int id = 0;
// Here we have "already read" the URLs from the local store.
foreach (var url in new string[]
{
"https://i.stack.imgur.com/eSiWr.png",
"https://i.stack.imgur.com/o5dmA.png",
"https://i.stack.imgur.com/aqAuN.png",
})
{
var dynamicPB = new PictureBox
{
Width = flowLayoutPanel.Width - SystemInformation.VerticalScrollBarWidth,
Height = 200,
SizeMode = PictureBoxSizeMode.Zoom,
BackColor = Color.Azure,
// In this case, the url is already stored here...
ImageLocation = url,
// ...but here are a couple of other ID options.
Name = $"pictureBox{++id}",
Tag = $"Tag Identifier {id}",
Padding = new Padding(10),
};
dynamicPB.Click += onAnyPictureBoxClick;
flowLayoutPanel.Controls.Add(dynamicPB);
}
}
.
.
.
}

How can I place a border color in a pictureBox if the image loaded in it is from the resources?

I set my pictureBox with a default image from resources with this:
public Form1()
{
InitializeComponent();
pictureBox1.Image = Properties.Resources.default_Employee_Image;
}
when I check if the picture in the pictureBox is from resources with this code, it doesn't draw the border:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
//pictureBox border color
Color themeColor = Color.FromArgb(172, 188, 212);
if (pictureBox1.Image == null) //if image from db is null display default image
{
pictureBox1.Image = Properties.Resources.default_Employee_Image;
}
//if image is the default image: paint border
if (pictureBox1.Image == Properties.Resources.default_Employee_Image)
{
ControlPaint.DrawBorder(e.Graphics, pictureBox1.ClientRectangle, themeColor, ButtonBorderStyle.Solid);
}
}
Also if image from the pictureBox is default, I'll save it as null in my database.
I want to only have a border if the default image is loaded. If I select another image the border should be gone.
If the pictureBox has the default image it should look like this (which means that I still have not selected a picture of the user):
But if the picture in my pictureBox is not the default image-(the image in my resources), [This means that I already selected an image] it should look like this:
And not like this:
pictureBox1.Image == Properties.Resources.SomeImage will never return true, because every time that you call Properties.Resources.SomeImage, it returns a new instance of the image.
You don't need to track the assigned image; instead, you need to track the status. You can use either of the following options:
Rely on a flag: You can set a flag like bool isDefaultImage = true; at form level and if at some point of your application you changed the image, set it to true. Something like this:
if(isDefaultImage)
{
//draw border
}
Rely on Model/DataSource: You can also rely on your model/data source values and instead of checking UI elements, check if the user has a profile picture. Something like this:
if(myUserObject.ProfilePicture == null)
{
//draw border
}
Compare two images
Anyhow, just in case you are interested to compare two Image objects to see whether they are same Image, you can use the following method:
public bool AreImagesEqual(Image img1, Image img2)
{
ImageConverter converter = new ImageConverter();
byte[] bytes1 = (byte[])converter.ConvertTo(img1, typeof(byte[]));
byte[] bytes2 = (byte[])converter.ConvertTo(img2, typeof(byte[]));
return Enumerable.SequenceEqual(bytes1, bytes2);
}
Then you can use it like this:
using(var image = Properties.Resources.DefaultImage)
{
var isDefaultImage = AreImagesEqual(pictureBox1.Image, image);
if(isDefaultImage)
{
//draw border
}
}
I have found a solution for my problem, I have changed my approach (Rely on a flag approach [by #Reza Aghaei]), and instead of comparing pictureBox image with the resource image, I just checked if pictureBox1.Tag is null or not:
On my constructor:
public Form1()
{
InitializeComponent();
pictureBox1.Image = Properties.Resources.default_Employee_Image; //the default image from resources
pictureBox1.Image = null;
}
On my dataGridView cell click event:
private void dataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.RowIndex != -1)
{
//Display user image
using (SqlConnection con = new SqlConnection(connectionStringConfig))
using (SqlCommand sqlCmd = new SqlCommand("SELECT user_image FROM dbo.Employee_Image WHERE employee_id=#employee_id", con))
{
con.Open();
sqlCmd.Parameters.Add("#employee_id", SqlDbType.NVarChar).Value = EmployeeId;
using (SqlDataReader reader = sqlCmd.ExecuteReader())
{
if (reader.HasRows)
{
reader.Read();
pictureBox1.Image = ImageOperations.BytesToImage((byte[])(reader.GetValue(0)));
if (reader.GetValue(0) == null) //if image is null add border color to tag
{
pictureBox1.Tag = my_color_here;
}
else
{
pictureBox1.Tag = null;
}
}
else
{
pictureBox1.Image = null;
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Something is wrong with the selected record! \nError: { ex.GetType().FullName }");
}
}
On BytesToImage method in ImageOperations class:
public static Image BytesToImage(byte[] buffer) //Get image from database
{
using (MemoryStream ms = new MemoryStream(buffer))
{
return Image.FromStream(ms);
}
}
And on my pictureBox paint event:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
//if image is not present in the picturebox -> paint its border
if (pictureBox1.Image == null)
{
pictureBox1.Tag = my_color_here;
pictureBox1.Image = Properties.Resources.default_Employee_Image;
}
if (pictureBox1.Tag != null) //if tag has a value -> paint the border (this happens if image form db is null)
{
ControlPaint.DrawBorder(e.Graphics, pictureBox1.ClientRectangle, my_color_here, ButtonBorderStyle.Solid);
}
}

Changing image value in DataGridViewImageColumn does not remove previous image

The problem:
When changing what image shows up in the cell of a DataGridViewImageColumn, the previous image is still there behind the new image:
(Note the red error behind the green checkmark)
What I have:
The image is showing status on connectivity to a machine. When the machine's status is updated an event is raised and the image is updated.
The declaration of the DataGridViewImageColumn:
DataGridViewImageColumn imc = new DataGridViewImageColumn
{
HeaderText = "C$",
Name = "imc",
Width = 25,
ImageLayout = DataGridViewImageCellLayout.Stretch,
ValuesAreIcons = true
};
Defaults are set:
sDGView.Columns["imc"].DefaultCellStyle.NullValue = null;
((DataGridViewImageCell)sDGView.Rows[0].Cells["imc"]).Value = null;
Event when a row is added:
private void SDGView_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
((DataGridViewImageCell)sDGView.Rows[LastRow].Cells["imc"]).Value = null;
}
Method called when online status changed:
private void SetStatusImage(int ri)
{
var status = Core.Machines[ri].OnLine;
//sDGView.Rows[ri].Cells["imc"].Dispose();
var image = (DataGridViewImageCell)sDGView.Rows[ri].Cells["imc"];
if (status is null)
{
image.Value = StatusImg[0];
}
else if (status is true)
{
image.Value = StatusImg[1];
}
else
{
image.Value = StatusImg[2];
}
}
The images:
public Icon[] StatusImg { get; private set; } = new Icon[]
{
Properties.Resources.Minus_Grey,
Properties.Resources.Tick_Green,
Properties.Resources.Error_Red
};
What I've tried...
I've tried setting the image.Value to null - no change
I've called the Dispose() method on the cell itself and then created a new cell to take its place and set the value to the current image -- the previous image still shows up behind!
It seems each time I change the value of the cell, another image is simply added on top. I can verify by watching the memory size of the program increase as the image changes. The old one never actually goes away even with calling the Dispose() method.
Edit:
I tried having the main form call the Refresh() method in case it just needed to be redrawn. - no change
I tried removing the image variable and setting the image directly:
private void SDGView_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
((DataGridViewImageCell)sDGView.Rows[LastRow].Cells["imc"]).Value = StatusImg[0];
}
private void SetStatusImage(int ri)
{
var status = Core.Machines[ri].OnLine;
//sDGView.Rows[ri].Cells["imc"].Dispose();
//var image = (DataGridViewImageCell)sDGView.Rows[ri].Cells["imc"];
if (status is null)
{
//image.Value = StatusImg[0];
((DataGridViewImageCell)sDGView.Rows[ri].Cells["imc"]).Value = StatusImg[0];
}
else if (status is true)
{
//image.Value = StatusImg[1];
((DataGridViewImageCell)sDGView.Rows[ri].Cells["imc"]).Value = StatusImg[1];
}
else
{
//image.Value = StatusImg[2];
((DataGridViewImageCell)sDGView.Rows[ri].Cells["imc"]).Value = StatusImg[2];
}
}
--same result - previous image still persists behind the newly selected image.
(Note the red error behind the green checkmark)
I tested such scenario with this simple code:
private void PlanningDayPlans_Load(object sender, EventArgs e)
{
DataGridViewRow r = new DataGridViewRow();
this.DataGridView1.Rows.Add(r);
}
private void Button1_Click(object sender, EventArgs e)
{
this.DataGridView1.Rows(0).Cells(0).Value = My.Resources.todo2;
}
private void Button2_Click(object sender, EventArgs e)
{
this.DataGridView1.Rows(0).Cells(0).Value = My.Resources.cross;
}
And it works without issues:
The pictures are PNG with transparent backgrounds (would show the problem), I can switch them back and forth and it works well.
This is for un-bound and DataSource-less scenario.
I think I know where your problem probably is created. You set the ValuesAreIcons property, which affects alpha channel of the background so that it is "correct" for icons. I would try to ommit this settings and use PNG icons, it will be fine.

Form ShowDialog won't load image in picture box

When I refresh my form with SHOWDIALOG my image doesn't load in my picture-box but if i refresh with only SHOW it works fine.
I want to be able to refresh with SHOWDIALOG and still have my picture load method work.
I have tried clearing data-bindings of both picture-box and the brows button.
private void formrefresh()
{
FoodItem FoodItem = new FoodItem();
FoodItem.ShowDialog();
this.Close();
}
public void GetImage()
{
OpenFileDialog BrowseImage = new OpenFileDialog();
BrowseImage.Filter = "Image Files(*.jpg; *.gif;)|*.jpg; *.gif";
if (BrowseImage.ShowDialog() == DialogResult.OK)
{
TextBox t =
Application.OpenForms["FoodItem"].Controls["imagePath"] as TextBox;
t.Text = BrowseImage.FileName;
filenametext = BrowseImage.FileName;
PictureBox p = Application.OpenForms["FoodItem"].Controls["foodImage"] as PictureBox;
p.Image = new Bitmap(BrowseImage.FileName);
}
}
private void BrowsImage_Click(object sender, EventArgs e)
{
GetFoodImage image = new GetFoodImage();
image.GetImage();
}
Finding forms (Application.OpenForms) is not a very good approach to use. Try to avoid that if possible. For example, there can be complications if there are multiple instances of a certain Form and you will have to find the exact instance you want to update.
It doesn't make much sense to use a Library to GetImage in your example. If you really need it in a separate Class Library, just return the path. Just return the image path from GetImage method and set the PictureBox from FoodImage.
private void formrefresh()
{
FoodItem foodItem = new FoodItem();
foodItem.ShowDialog();
this.Close();
}
private void BrowsImage_Click(object sender, EventArgs e)
{
GetFoodImage image = new GetFoodImage();
var imagePath = image.GetImage();
this.foodImage.Image = new Bitmap(imagePath);
this.imagePath.Text = imagePath;
}
public string GetImage(FoodItem foodItem)
{
OpenFileDialog BrowseImage = new OpenFileDialog();
BrowseImage.Filter = "Image Files(*.jpg; *.gif;)|*.jpg; *.gif";
if (BrowseImage.ShowDialog() == DialogResult.OK)
{
return BrowseImage.FileName;
}
return "";
}

c# . if (image == Properties.Resources.image)

I want to do an if stament on an image
if (SortName.Image == Properties.Resources.RadioEmpty)
{
SortName.Image = Properties.Resources.Radio;
}
else
{
SortName.Image = Properties.Resources.RadioEmpty;
}
but its on working any idea what I'm doing wrong ?
o.k additional information
1.
//SortName = A picture box
//Properties.Resources.RadioEmpty = Resources\RadioEmpty.png
//Properties.Resources.Radio = Resources\Radio.png
2.
Nope no errors
3.I wanted to use a custom image for a radio button. A have a picture box with the above code on click. RadioEmpty is the default so I check to see if the picturebox's image is the same as image form the Resources folder is so do code.
i advise you use tag for this issue see this code
private void Form1_Load(object sender, EventArgs e)
{
//in form load the radio is checked or unckecked
//here my radio is unchecked at load
pictureBox1.Image = WindowsFormsApplication5.Properties.Resources.Add;
pictureBox1.Tag = "UnChecked";
}
private void pictureBox1_Click(object sender, EventArgs e)
{
//after pictiurebox clicked change the image and tag too
if (pictureBox1.Tag.ToString() == "Checked")
{
pictureBox1.Image = WinFormsApplication.Properties.Resources.Add;
pictureBox1.Tag = "UnChecked";
}
else
{
pictureBox1.Image = WinFormsApplication.Properties.Resources.Delete;
pictureBox1.Tag = "Checked";
}
}
compare the names. Something like this (unverified)
if (SortName.Image.Name.Equals(Properties.Resources.RadioEmpty.Name))
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Bitmap bm1;
Bitmap bm2;
private void button1_Click(object sender, EventArgs e)
{
bm1 = new Bitmap(Properties.Resources.firegirl1);
bm2 = new Bitmap(Properties.Resources.Zemli2);
pictureBox1.Image = bm1;
pictureBox2.Image = bm2;
if (pictureBox1.Image==pictureBox2.Image)
{
MessageBox.Show("Some");
}
else
{
MessageBox.Show("Differ");
}
}
}

Categories

Resources