c# Listview with images column strange behaviour - c#

i have a list view in which i load some images from a folder through imagelist control.
when I initialize the form containing the listview i call a method CaricaListBox()
private void CaricaListBox()
{
if (TxtSourceFolder.FullText != string.Empty)
{
LstFile.Items.Clear();
foreach (string file in Directory.EnumerateFiles(TxtSourceFolder.FullText, "*.*").Where(s => s.EndsWith(".png") || s.EndsWith(".jpg")))
{
Image ImgObj;
using (var bmpTemp = new Bitmap(file))
{
ImgObj = new Bitmap(bmpTemp);
}
if (ImgObj.Width > ImgObj.Height)
{
imageList.Images.Add(Image.FromFile(file));
var LstI = LstFile.Items.Add(" ", imageList.Images.Count - 1); //(Path.GetFileName(file), imageList.Images.Count - 1);
LstI.SubItems.Add(Path.GetFileName(file));
}
ImgObj.Dispose();
}
}
}
when the form is shown it is as follow
and this is what i want.
If press the change folder and i run again the same method to load listbox
i have follow result :
showing only 1 column.
if i enlarge the listbox view i see as follow :
so nothing changes
what am I doing wrong ?
any suggestion ?
Tks in advance
Fabrizio
Edited:
private void CaricaListBox()
{
if (TxtSourceFolder.FullText != string.Empty)
{
LstFile.Items.Clear();
foreach (string file in Directory.EnumerateFiles(TxtSourceFolder.FullText, "*.*").Where(s => s.EndsWith(".png") || s.EndsWith(".jpg")))
{
var bmpTemp = Image.FromFile(file);
if (bmpTemp.Width > bmpTemp.Height)
{
using (var tempImage = bmpTemp)
{
Bitmap bmp = new Bitmap(192, 76);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(tempImage, new Rectangle(0, 0, bmp.Width, bmp.Height));
}
imageList.Images.Add(bmp);
var LstI = LstFile.Items.Add(" ", imageList.Images.Count - 1);
LstI.SubItems.Add(Path.GetFileName(file));
}
}
else bmpTemp.Dispose();
}
}
Conta();
}
it saves memory .. but still have visualization problem when the method is called for the second time.
just noticed that if i call method while form is not yet drawn, it shows 2 columns, if i call method when the form is already drawn, i see 1 column
Fabrizio

Related

c# win app forms detect the number of faces on image and prompt error message if there is more than 1 face

I am currently doing an ID pic uploading system using C# Windows App Forms and I would like to allow the user to upload an image and the image must only contain 1 front face. To prevent user from uploading more than 1 face, I would like to prompt them an error message once the system detects more than one face in an image but I am not sure how to go about it. I used takuya takeuchi's dlibdotnet library.
Here is my current code.
namespace DetectTrial
{
public partial class Form1 : Form
{
#region Fields
private readonly BackgroundWorker _BackgroundWorker;
#endregion
#region Constructors
public Form1()
{
this.InitializeComponent();
this._BackgroundWorker = new BackgroundWorker();
this._BackgroundWorker.DoWork += this.BackgroundWorkerOnDoWork;
}
#endregion
#region Methods
#region Event Handlers
private void BackgroundWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
var path = doWorkEventArgs.Argument as string;
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
return;
using (var faceDetector = Dlib.GetFrontalFaceDetector())
using (var ms = new MemoryStream(File.ReadAllBytes(path)))
using (var bitmap = (Bitmap)Image.FromStream(ms))
{
using (var image = bitmap.ToArray2D<RgbPixel>())
{
var dets = faceDetector.Operator(image);
foreach (var g in dets)
Dlib.DrawRectangle(image, g, new RgbPixel { Green = 255 }, thickness: 10);
var result = image.ToBitmap();
this.pictureBox1.Invoke(new Action(() =>
{
this.pictureBox1.Image?.Dispose();
this.pictureBox1.Image = result;
}));
}
}
}
private void button1_Click(object sender, EventArgs e)
{
using (var opnfd = new OpenFileDialog())
{
opnfd.Filter = "Image Files (*.jpg;*.jpeg;*.png;)|*.jpg;*.jpeg;*.png";
if (opnfd.ShowDialog(this) == DialogResult.OK)
{
this._BackgroundWorker.RunWorkerAsync(opnfd.FileName);
}
}
}
#endregion
#endregion
}
}
I don't know where to go from here.
I'm not familiar with the library you're using, but if dets is a collection of detected face rectangles, you can probably use something like this:
var dets = faceDetector.Operator(image);
if (dets.Count() > 1)
{
MessageBox.Show("Too many faces! Why are there so many faces? I can't look. Please make it stop.");
return;
}
else
{
var g = dets.First();
Dlib.DrawRectangle(image, g, new RgbPixel { Green = 255 }, thickness: 10);
var result = image.ToBitmap();
this.pictureBox1.Invoke(new Action(() =>
{
this.pictureBox1.Image?.Dispose();
this.pictureBox1.Image = result;
}));
}
Note that Count() and First() are extension methods from System.Linq so you'll need to make sure there's a using System.Linq; directive at the top of your code file.
Also, the Invoke code is probably better moved to the BackgroundWorker's OnRunWorkerCompleted event (where the cross-thread invoke will no longer be needed) and you can access the PictureBox directly.

Combined image from List<Bitmap> images not appearing in ImageColumn

On my current project, there are several instances where a row of indicators (using 26x26 PNG icons) is required. In this case, it's a DataGridView (more specifically, a Telerik RadGridView). The images are loaded from the resource file and are present.
My current code defines these indicators as a row is created:
Bitmap indicators;
List<Bitmap> content = new List<Bitmap>();
if (l.Condition1 == true)
content.Add(myProj.Properties.Resources.One);
if (l.Condition2 == true)
content.Add(myProj.Properties.Resources.Two);
if (l.Condition3 == true)
content.Add(myProj.Properties.Resources.Three);
if (content.Count() != 0)
{
int width = content.Select(w => w.Width).Sum();
indicators = new Bitmap(width, 26);
using (Graphics g = Graphics.FromImage(indicators))
{
int origin = 0;
foreach (Bitmap p in content)
{
g.DrawImage(p, new Point(origin, 0));
origin += p.Width;
}
row.Cells[15].Value = indicators;
}
}
Unfortunately, this code doesn't work (the images don't display at all) and I can't quite sort out what I've missed. Assigning an image to the Value property works if I just load one image and don't use the loop. What have I overlooked that is preventing this from working and these indicators displaying?

Creating ImageList object in a loop

I have very simple logic for list view items creating. I'm storing all the col headers name from dataGridView into ColNamesForm1 array and then only comparing if last 5 characters match with (num) or (cat) string. For these two options I'm appending different picture into listview lvMoveFrom using of Populate function stored in static classs OtherFunctions.
But something is wrong in my code because after last iteration it is appending images from the last column - if the first column is (num) and the last column is (cat) the images in listview are all the same - from cat image.
How can I fix this issue? I was wondering about creating new ImageList object for every columns but I have no idea, how can I do that dynamically, e.g. using of loop index i.
Please, can you help to solve my problem.
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < ColNamesForm1.Length; i++)
{
if (ColNamesForm1[i].ToString().Substring(0, 4).ToUpper() != "COL_" && Regex.IsMatch(ColNamesForm1[i].ToString().Substring(4, 1), #"^\d+$") == false)
{
if (ColNamesForm1[i].ToString().Substring(ColNamesForm1[i].ToString().Length - 5) == "(num)")
{
OtherFunctions.Populate(lvMoveFrom, ColNamesForm1[i].ToString(), #"C:\pictures\num.png");
}
else
{
OtherFunctions.Populate(lvMoveFrom, ColNamesForm1[i].ToString(), #"C:\pictures\cat.png");
}
}
}
}
public static void Populate(ListView lv, string itemName, string pathToPicture)
{
ImageList img = new ImageList();
img.ImageSize = new Size(30, 30);
img.Images.Add(Image.FromFile(pathToPicture));
lv.SmallImageList = img;
lv.Items.Add(itemName, 0);
}
The Problem
So basically this:
ImageList img = new ImageList();
img.ImageSize = new Size(30, 30);
img.Images.Add(Image.FromFile(pathToPicture));
lv.SmallImageList = img;
lv.Items.Add(itemName, 0);
Was adding a new image list to the ListView. The same ListView you passed it everytime (so you were effectively overwriting it). Secondly, the line:
lv.Items.Add(itemName, 0);
The second argument is the index in the image list (that you assigned to the ListView). So giving it 0 will ask the ListView to pick the image from lv.SmallImageList[0] (psuedo code) basically.
The Solution
To remove the overwriting I pulled the image setup logic out of the Populate and put it back in the main method. I'll break down just the setup logic:
ImageList img = new ImageList();
img.ImageSize = new Size(30, 30);
var paths = new List<string> { #"C:\pictures\num.png", #"C:\pictures\cat.png" };
paths.ForEach(path => img.Images.Add(MediaTypeNames.Image.FromFile(path)));
lvMoveFrom.SmallImageList = img;
I put all the image paths into a List<string> and just used the LINQ ForEach to iterate over each one adding it to the ImageList img. There is no difference from your original code, apart from I add all the images to the ListView and I only do it once.
So then to make your code easier to understand I did some simple refactorings.
First was invert if statement:
if (ColNamesForm1[i].ToString().Substring(0, 4).ToUpper() != "COL_" && Regex.IsMatch(ColNamesForm1[i].ToString().Substring(4, 1), #"^\d+$") == false)
To:
if (ColNamesForm1[i].ToString().Substring(0, 4).ToUpper() == "COL_"
|| Regex.IsMatch(ColNamesForm1[i].ToString().Substring(4, 1), #"^\d+$"))
{
continue;
}
This is almost like a guard clause, it says if we don't meet these minimum conditions then move to the next item.
Then I simplified your method execution by reducing duplication:
if (ColNamesForm1[i].ToString().Substring(ColNamesForm1[i].ToString().Length - 5) == "(num)")
{
OtherFunctions.Populate(lvMoveFrom, ColNamesForm1[i].ToString(), #"C:\pictures\num.png");
}
else
{
OtherFunctions.Populate(lvMoveFrom, ColNamesForm1[i].ToString(), #"C:\pictures\cat.png");
}
To:
var image = ColNamesForm1[i].ToString().EndsWith("(num)")
? 0 // corresponds with the position of the image in the ImageList
: 1;
OtherFunctions.Populate(lvMoveFrom, ColNamesForm1[i].ToString(), image);
Finally you will see that I change your Populate method. Firstly we are prepopulating your ListView with the images, then using that ternary operator to choose which index of the image to show.
The code all together is:
private void Form1_Load(object sender, EventArgs e)
{
ImageList img = new ImageList();
img.ImageSize = new Size(30, 30);
var paths = new List<string> { #"C:\pictures\num.png", #"C:\pictures\cat.png" };
paths.ForEach(path => img.Images.Add(Image.FromFile(path)));
lvMoveFrom.SmallImageList = img;
for (int i = 0; i < ColNamesForm1.Length; i++)
{
if (ColNamesForm1[i].ToString().Substring(0, 4).ToUpper() == "COL_"
|| Regex.IsMatch(ColNamesForm1[i].ToString().Substring(4, 1), #"^\d+$"))
{
continue;
}
var image = ColNamesForm1[i].ToString().EndsWith("(num)")
? 0 // corresponds with the position of the image in the ImageList
: 1;
OtherFunctions.Populate(lvMoveFrom, ColNamesForm1[i].ToString(), image);
}
}
public static void Populate(ListView lv, string itemName, int imageIndex)
{
lv.Items.Add(itemName, imageIndex);
}
Now you can simplify further:
private void Form1_Load(object sender, EventArgs e)
{
ImageList img = new ImageList();
img.ImageSize = new Size(30, 30);
var paths = new List<string> { #"C:\pictures\num.png", #"C:\pictures\cat.png" };
paths.ForEach(path => img.Images.Add(Image.FromFile(path)));
lvMoveFrom.SmallImageList = img;
for (int i = 0; i < ColNamesForm1.Length; i++)
{
if (ColNamesForm1[i].ToString().Substring(0, 4).ToUpper() == "COL_"
|| Regex.IsMatch(ColNamesForm1[i].ToString().Substring(4, 1), #"^\d+$"))
{
continue;
}
var image = ColNamesForm1[i].ToString().EndsWith("(num)")
? 0 // corresponds with the position of the image in the ImageList
: 1;
lvMoveFrom.Items.Add(ColNamesForm1[i].ToString(), image);
}
}

Not all MapIcons get updated on map?

Im adding multiple MapIcons to my UWP app MapControl. I want to change the MapIcon size based map zoom level.
This works almost ok, but when I update marker sizes (call UpdatMarkerSizes()) not all MapIcons get new image.
I have around 200 MapIcons in my map, which I try to update. Is this some performance issue or how I should try to update all MapIcons?
Here is how I initially add MapIcons:
public void AddMapIcons(IReadOnlyCollection<IItem> items)
{
var icon = (CurrentMarkerIconSize == MarkerIconSize.Small) ? _markerSmall : _markerNormal;
foreach (var item in items)
{
var stopIcon = new MapIcon
{
Location = new Geopoint(item.Location),
NormalizedAnchorPoint = new Point(0.5, 0.5),
ZIndex = 5,
CollisionBehaviorDesired = MapElementCollisionBehavior.RemainVisible,
Image = icon
};
MapControl.MapElements.Add(stopIcon);
}
}
And here is my MapIcon Image update functionality, this is called when map zoomlevel is changed.
private void UpdatMarkerSizes(MarkerIconSize newSize)
{
var newImage = (newSize == StopIconSize.Small) ? _markerSmall : _markerNormal;
foreach (var element in MapControl.MapElements)
{
(element as MapIcon).Image = newImage;
}
}
UPDATE 1:
I just noticed that if I also update MapIcon NormalizedAnchorPoint, ZIndex and CollisionBehaviorDesired (set same value again), this issue is not so bad. Previously when I updated my markers (UpdatMarkerSizes()) there was always couple of markers which did not get the new image. Now after this update, I get rarely one or two markers which did not get new image.
Updated code:
private void UpdatMarkerSizes(MarkerIconSize newSize)
{
var newImage = (newSize == StopIconSize.Small) ? _markerSmall : _markerNormal;
foreach (var element in MapControl.MapElements)
{
var mapIconElement = element as MapIcon;
if(mapIconElement != null)
{
mapIconElement.Image = newImage;
mapIconElement.NormalizedAnchorPoint = new Point(0.5, 0.5);
mapIconElement.ZIndex = 5;
mapIconElement.CollisionBehaviorDesired = MapElementCollisionBehavior.RemainVisible;
}
}
}
UPDATE 2:
I think that my solution in update 1 is good enough for now. I have posted new question which is related to MapIcons and their image updating too: Dynamically update collection of MapIcons, update process gets out of sync?

Determine if two images are identical

Is there any way to determine if two images are identical?
I want to change an image every time my timer ticks (animation).
But, I need to see which image is displaying, so is there any way to compare 2 images
to do what I want?
if (myImage.Flags == (Image.FromFile(#"Images/Enemy.png").Flags))
{
myImage = Image.FromFile(#"Images/Enemy2.png");
}
else
{
myImage = Image.FromFile(#"Images/Enemy.png");
}
Don't compare the images, just maintain the index of the current image in a variable.
Here's an example that works for any number of images:
private int _currentImageIndex;
private string[] _imagePaths =
{
"Images/Enemy.png",
"Images/Enemy2.png",
"Images/Enemy3.png",
};
...
void NextImage()
{
// Dispose the current image
Image img = pictureBox1.Image;
pictureBox1.Image = null;
if (img != null)
img.Dispose();
// Show the next image
_currentImageIndex = (_currentImageIndex + 1) % _imagePaths.Length;
string path = _imagePaths[_currentImageIndex];
pictureBox1.Image = Image.FromFile(path);
}
I'd try to compare ImageLocation. Although it doesn't work if you have your pictures as resources.
if (PictureBox1.ImageLocation == PictureBox2.ImageLocation)
{
}
See my question:
Dynamically changing image in a picturebox
Here goes simple answer.
In case of just 2 images, use flag
// field, true if enemy2.png is loaded
bool _image2;
// somewhere
if(_image2)
{
myImage = Image.FromFile(#"Images/Enemy.png");
_image2 = false;
}
else
{
myImage = Image.FromFile(#"Images/Enemy2.png");
_image2 = true;
}

Categories