שאלה:
יש לי פקד CheckBoxList עם מס' פריטים בתוכו.
אני מעוניין שבאירוע מסויים, כמו למשל CLICK, הפריטים ב-CheckBoxList שלא ניבחרו על ידי המשתמש, יימחקו. כתבתי את הקוד הבא:
for(int i = 1;i<CheckBoxList1.Items.Count;i++)
{
if(!CheckBoxList1.Items[i].Selected)
{
CheckBoxList1.Items.Remove(CheckBoxList1.Items[i]);
Label1.Text = CheckBoxList1.Items.Count.ToString();
}
שאני מריץ לולאת for, אני מקבל כל מיני התנהגויות מוזרות וזה בכלל לא עושה מה שרציתי. איך אפשר לעשות כזה דבר?
תשובה:
(מוטי מאירי מוסיף כי במהלך ריצת הלולאה Count של המערך קטן)
Count אכן ילך ויקטן במהלך ביצוע הלולאה.
עדיף להימנע מביצוע לולאות for כאשר הערך עליו שואפים הוא גבול משתנה היות ותיווצר התנהגות בלתי-סדירה (כמו זו שהוצגה בתחילת השרשור).
כמו כן, בלולאות foreach לא ניתן להסיר אלמנטים עליהם עוברים.
ובהתאם, הפתרון הוא לולאת while.
בלולאה נקבל החלטה על האם ברצוננו להיפתר מהאיבר הנוכחי.
אם נרצה להיפתר מהאיבר הנוכחי - ניפתר ממנו ולא נקדם את המצביע לאיבר הנוכחי כי עכשיו במקומו כבר יש איבר אחר.
אם נרצה להשאיר את האיבר הנוכחי - נקדם את המצביע לאיבר הנוכחי לאיבר הבא.
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
//// not good!
//for (int i = 0; i < list.Count; i++)
// list.Remove(i + 1);
int i = 0;
while(i < list.Count)
{
bool ShouldWeRemoveSomethingFromList = (i%2 == 0);
if (ShouldWeRemoveSomethingFromList)
list.Remove(list[i]);
else
i++;
}
זה הסינטקס המקובל למעבר על אוספים כאשר משנים את גודלם, לא יהיה קשה להמיר את זה לעבור על מערך של ListItem ולשנות את התנאי ב-ShouldWeRemoveSomethingFromList.
היות ושתי סוגי הלולאות האחרות לא מתאימות מטעם הגדרתן למצב הזה, חובה לעבוד עם While.
אפשרות נוספת היא כתיבת מערך בצד שיצבור את הפריטים שרוצים להוריד בצד, ולאחר מכן נעבור עליו ונוריד את כל האלמנטים שנמצאים במערך בצד.
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
List<int> IntsToRemoveFromList = new List<int>();
foreach(int curIntToCheckIfWeNeedToRemove in list)
if (curIntToCheckIfWeNeedToRemove % 2 == 0)
IntsToRemoveFromList.Add(curIntToCheckIfWeNeedToRemove);
foreach (int curIntToRemove in IntsToRemoveFromList)
list.Remove(curIntToRemove);
יאיר נבט מוסיף כי באמצעות הסינטקס של לולאת While ניתן להוריד אלמנטים מסומנים מתוך CheckBoxList כך:
private void Button1_Click(object sender, System.EventArgs e)
{
int i = 0;
while(i < CheckBoxList1.Items.Count)
{
if (!CheckBoxList1.Items[i].Selected)
{
CheckBoxList1.Items.Remove(CheckBoxList1.Items[i]);
}
else
{
i++;
Label1.Text = CheckBoxList1.Items.Count.ToString();
}
}
עדכון:
חגי מוסיף כי ניתן לבצע לולאת for יורדת שמורידה איברים לפי מיקום וזה גם פתרון (ואף יותר טוב מאלו שהצעתי כאן) לבעיה:
for(int i = CheckBoxList1.Items.Count-1;i >=0;i--)
if (!CheckBoxList1.Items[i].Selected)
CheckBoxList1.Items.RemoveAt(i);
קישור: http://www.tapuz.co.il/tapuzforum/main/Viewmsg.asp?forum=831&msgid=89124134