I have a Dictionary
with pairs of strings
and Bitmaps
that corresponds to a ListView
control that lists all the image keys, among other info. Also I have a PictureBox
that must show the corresponding image of the dictionary when the listview's index changes. Multiselect is set to false. The DateTime
is there in order to ensure that only the most recent "selected" image is loaded.
When the index changes, randomly one of this things happens:
- The image loads correctly
- No change occurs on the
Picturebox
- A System.ArgumentException is thrown. The exception is always thrown in the same line (see code)
How can I prevent #2 and #3 happening?
Here is the code:
private Dictionary<string, Bitmap> dictionary;
private ReaderWriterLockSlim dictionary_lock;
private DateTime _LAST_PREVIEW_UPDATE;
private DateTime LAST_PREVIEW_UPDATE
{
get { return _LAST_PREVIEW_UPDATE; }
set
{
if(value.CompareTo(_LAST_PREVIEW_UPDATE) < 0)
_LAST_PREVIEW_UPDATE = value;
}
}
/* ... */
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.listView1.SelectedItems.Count != 1)
return;
DateTime now = DateTime.Now;
string image_code = this.listView1.SelectedItems[0].SubItems[0].Text;
Thread t = new Thread(() =>
{
Bitmap image = null;
bool load_properly;
dictionary_lock.EnterReadLock();
try
{
load_properly = dictionary.TryGetValue(image_code, out image);
}
finally
{
dictionary_lock.ExitReadLock();
}
if (!load_properly)
{
/* LOG THE ERROR */
return;
}
this.BeginInvoke((MethodInvoker)delegate
{
if (now.CompareTo(LAST_PREVIEW_UPDATE) > 0)
{
LAST_PREVIEW_UPDATE = now;
if (picturebox.Image != null)
picturebox.Image.Dispose();
picturebox.Image = image; // This throws the exception
}
});
});
lock (picturebox)
{
t.Start();
t.Join(3000);
}
}
EDIT: Here is the call stack
System.Drawing.dll!System.Drawing.Image.FrameDimensionsList.get() + 0x258 bytes
System.Drawing.dll!System.Drawing.ImageAnimator.CanAnimate(System.Drawing.Image image) + 0x6d bytes
System.Drawing.dll!System.Drawing.ImageAnimator.ImageInfo.ImageInfo(System.Drawing.Image image) + 0x26 bytes
System.Drawing.dll!System.Drawing.ImageAnimator.Animate(System.Drawing.Image image, System.EventHandler onFrameChangedHandler) + 0x97 bytes
System.Windows.Forms.dll!System.Windows.Forms.PictureBox.Animate(bool animate) + 0x65 bytes
System.Windows.Forms.dll!System.Windows.Forms.PictureBox.InstallNewImage(System.Drawing.Image value, System.Windows.Forms.PictureBox.ImageInstallationType installationType) + 0xc8 bytes
> Application.exe!Application.FrmMain.listView1_SelectedIndexChanged.AnonymousMethod__a() Line 596 + 0x78 bytes C#
EDIT2: I want to implement the functionality explained before, but this is the better approach I could get. Calling directly from the UI-thread was too slow (evidently) so I tried executing it from another thread. I want the image to represent the last selected item on the list view, but simply summoning threads could (and would) cause a race condition, so I need a mechanism to prevent/discard old selected indexes from changing the image, so only the last selected index decides which image to load.
Thanks for your time!